XSS
1. 개요
Cross-site Scripting.[1]
SQL injection과 함께[2] 웹 상에서 가장 기초적인 취약점 공격 방법의 일종으로, 악의적인 사용자가 공격하려는 사이트에 스크립트를 넣는 기법을 말한다. 공격에 성공하면 사이트에 접속한 사용자는 삽입된 코드를 실행하게 되며,[3] 보통 의도치 않은 행동을 수행시키거나 쿠키나 세션 토큰 등의 민감한 정보를 탈취한다.
공격 방법에 따라 Stored XSS와 Reflected XSS로 나뉜다. Stored XSS는 사이트 게시판이나 댓글, 닉네임 등 스크립트가 서버에 저장되어 실행되는 방식이고, Reflected XSS는 보통 URL 파라미터(특히 GET 방식)에 스크립트를 넣어 서버에 저장하지 않고 그 즉시 스크립트를 만드는 방식이다. 후술된 내용 대부분은 Stored XSS라고 생각하면 된다. Reflected XSS의 경우 브라우저 자체에서 차단하는 경우가 많아 상대적으로 공격을 성공시키기 어렵다.
크로스 사이트 스크립팅이란 이름 답게, 자바스크립트를 사용하여 공격하는 경우가 많다. 공격 방법이 단순하고 가장 기초적이지만, 많은 웹사이트들이 XSS에 대한 방어 조치를 해두지 않아 공격을 받는 경우가 많다. 여러 사용자가 접근 가능한 게시판 등에 코드를 삽입하는 경우도 많으며, 경우에 따라서는 메일과 같은 매체를 통해서도 전파된다. 심지어는 닉네임에 코드를 심기도 한다.
물론, HTML을 사용하는 것이기 때문에, Text-Only 게시판이나, BBCode를 이용하는 위키위키 등에서는 XSS가 발생할 일은 없다. 단, 나무위키의 경우
{{{#!html HTML}}}
을 이용해서 HTML 태그를 사용할 수 있으므로 취약점이 있을 수도 있다. 그래서 나무위키 초반에는 스크립트 태그와 이벤트 속성도 막지 않았다. 물론, 이는 후에 대부분 수정되었다.주로 CSRF를 하기 위해서 사용되기 때문에 종종 CSRF와 혼동되는 경우가 있으나, XSS는 자바스크립트를 실행시키는 것이고, CSRF는 특정한 행동을 시키는 것이므로 다르다.
2. 예시
해커의 홈페이지에 PHP 파일을 만들고,[4] 공격할 사이트에 스크립트를 심은 뒤,[5] 사용자가 스크립트가 심어진 페이지를 볼 경우, 쿠키값이 해커의 홈페이지로 넘어가서 세션 하이재킹이 가능해진다.
3. 기법
아래는 한국에서 가장 많이 사용되는 IE 기준으로 작성한 것이다. 따라서 크롬, 파이어폭스 등의 브라우저나 최신 브라우저 환경에서는 작동하지 않을 수도 있다.
실험은 이 곳에서 해보자.
3.1. 스크립트 태그
3.2. 자바스크립트 링크
3.3. 이벤트 속성
3.4. 블랙리스트 우회
3.5. 내용 난독화
3.6. 스크립트 난독화
4. 방어 방법
XSS는 데이터를 입력할 때와 출력할 때, 모두 필터링하고, 클라이언트에도 막을 수 있을만한 수단을 구성해놓는 것이 좋다. 보안은 한없이 덧대도 끝이 없고, 아래 서술한 방식으로 구성해도 기어코 뚫어버리는 사람들이 존재하기 때문이다.
4.1. 입력 필터
자바는 적당한 XSS 필터를 만든 뒤, web.xml에 선언하여 모든 파라미터가 해당 필터를 거치도록 하는것 만으로도 좋은 효과를 볼 수 있다.
PHP는 입력을 처리할 때, 정규식을 이용하는 preg_replace를 사용하거나, 노드를 이용하는 DOMDocument를 사용할 수 있다. 속도는 preg_replace가 더 빠르지만, 정규식의 경우 예외와 오류가 종종 발생하기 때문에, 더 안전한 DOMDocument를 사용하는 것이 권장된다.[8]
4.1.1. 필터 제작
단순히 텍스트만 입력시키거나 출력하는 데 필터를 제작할 때, 주로 필터되는 것이 <와 >이다. 각각
&
lt;
와 &
gt;
처럼 HTML 문자로 바꾸어서 HTML 코드가 아닌 단순 문자로 인식하게 하는 것이다. 하지만, 이 경우는 모든 HTML 태그를 막아버리기 때문에, 각종 스타일을 적용시켜야하는 사이트에서는 맞지 않을 수도 있다.4.1.1.1. 라이브러리 제작
각종 스타일을 적용시켜 글을 꾸며야 하는 사이트의 경우, 스크립트 태그와 이벤트 속성,
javascript:
링크만 제대로 막아주어도 상당수를 막을 수 있을 것이다. 하지만, EMBED 태그를 이용하여 저런 기법을 사용하지 않고도 XSS를 먹이는 경우가 있으므로, 다른 것들도 모두 막아주어야 한다.이 때, 일부 태그 및 속성만 막기 보다는, 반대로 일부 태그 및 속성만 허용하게 하는 것이 좋다. 몇몇 태그만 막는다면 HTML 태그나 속성이 추가될 수록 주기적으로 필터를 수정해줘야 하는데, 이럴 경우 HTML 표준을 주기적으로 꼼꼼히 챙겨보지 않는 한은[9] , 업데이트를 하는 사이에 이미 해커가 침투해놓고 유유자적하게 빠져나갔을 수도 있다.
4.1.1.2. 라이브러리 이용
기존에 있던 라이브러리를 가져다 사용해도 좋다. 개인이나 소규모 단체가 만든 것 보다는, 전문적인 보안 업체나 거대 기업에서 만든 것이 공신력있고 편하기 때문이다. 사용할만한 것으로는 OWASP Antisamy이나 NAVER Lucy XSS Filter, ESAPI 등을 이용하는 방법이 있다.
4.1.1.3. BBCode 사용
글을 꾸며야 하지만, 따로 필터를 만들긴 어려운 경우, BBCode를 사용할 수도 있다.[10] 대부분의 위키에서[11] 도 이 방법을 사용하기도 하니, 지금 이 글을 보고 있는 당신이라면 상당히 익숙할 수도 있겠다, 만들기도 다른 방법에 비해 편하고, 안전성도 높은 편이기 때문에 가장 추천되는 방법이다. 또한 <나 >같은 HTML 코드를 단순 문자로 바꿀 수 있기 때문에 XSS가 실행될 염려가 적다. 하지만 이 때, 정규식과 같이 단순히 문자열 치환으로 수정할 경우, XSS가 발생할 수도 있다.
4.2. 출력 필터
HTML5부터 iframe의 sandbox 옵션으로 iframe 내의 자바스크립트, 폼과 같은 것의 제한이 가능해 졌으므로, 위의 방법을 실행한 뒤 2차적으로 써보는 것도 좋다. 단, 어디까지나 2차로만 사용하는 것이 좋다. 구버전의 웹 브라우저라면 호환이 되지 않아 스크립트가 그대로 실행되거나, 설령 그게 아니더라도 웹 브라우저에서 취약점이 발견될 경우, 이런 방법이 뚫릴 수 있기 때문이다.
iframe은 단순히 다른 웹사이트를 불러오는 것만이 아니라, 부모 창에서 마음대로 수정할 수 있기 때문에, 무식하게 다른 페이지를 불러오며 AJAX나 PJAX 등을 포기하며 사용하지 말자. 물론, 기존부터 라이브러리를 사용하지 않고 웹 페이지를 개발하는 사람 은 물론이고, jQuery같은 라이브러리를 사용하는 사람들도 조금만 검색해보면 간단하게 iframe의 내용을 수정할 수 있는 방법이 나오니 도전해 보도록 하자.
4.3. 쿠키의 보안 옵션 사용
쿠키 생성시 '보안 쿠키'라는 파라미터를 지정하면 TLS 상에서만 사용하게 할 수 있으며, 'HTTP ONLY'라는 파라미터로 웹 브라우저상에서만 쓸 수 있게 할 수도 있다. 물론 완전히 방어가능한 건 아니니[12] 위의 해결법과 병행하는 것이 좋다.
4.4. 콘텐츠 보안 정책 (Content Security Policy, CSP) 사용
스크립트 실행에 대한 정책(조건)을 설정해 알 수 없는 스크립트가 실행되는 것을 예방할 수 있다. 원래는 외부 서버의 스크립트 파일이 script 태그나 iframe 등 사이트에 로드가 되면 스크립트가 실행되지만, CSP를 설정해 출처가 자기 서버인 스크립트만 실행되도록 할 수 있다. 물론 이는 어디까지나 완전 방어가 아닌 예방의 차원이라는 것을 알아둬야 한다.
5. 기타
위 방법들과는 별개로 일반 사용자가 스크립트를 잘 알지 못하는 것을 악용하여, 사용자가 브라우저 콘솔에 악성 스크립트를 삽입해 실행되도록 속이기도 하는데, 이를 스스로 하는 XSS라는 뜻의 Self XSS라고 부른다. 이는 사용자가 스스로 실행한 것이니만큼 위 방어 방법으론 막기 어려우니, 잘 알지 못하는 스크립트 등은 절대로 실행해서는 안 된다.[13]
OWASP에서 XSS 공격을 방지하는 7계명을 발표하기도 했다.
파이어폭스 버그질라0. 허용된 위치가 아닌 곳에 신뢰할 수 없는 데이터가 들어가는것을 허용하지 않는다.
1. 신뢰할 수 없는 데이터는 검증을 하여라.
2. HTML 속성에 신뢰할 수 없는 데이터가 들어갈 수 없도록 하여라.
3. 자바스크립트에 신뢰할 수 없는 값이 들어갈 수 없도록 하여라.
4. CSS의 모든 신뢰할 수 없는 값에 대해서 검증하여라.
5. URL 파라미터에 신뢰할 수 없는 값이 있는지 검증하여라.
6. HTML 코드를 전체적으로 한번 더 검증하여라.
6. 관련 문서
[1] 줄이면 CSS인데 Cascading Style Sheet와 혼동이 올 수 있어서 XSS라고 부르는 것이다.[2] 사실 XSS와 SQL Injection은 그 경계가 모호하다. XSS가 javascript를 이용해서 공격한다면 SQL Injection은 파라미터를 통해 잘못된 Query가 실행하게 하는 정도의 차이고 둘은 공격방식이나(url을 조작해서 sql 인젝션하는 경우) 방어방식이나 상당히 유사하다. [3] 사용자 입장에서는 삽입된 코드가 원래부터 존재하는 코드인지 아닌지 분간 할 수 없다.[4] 예시에서는 Cookie.php라는 이름의 PHP 파일을 생성한다. 코드 -
fwrite(fopen("XSS.txt","a+"), $_GET['cookie']); ?>
[5] 예시에서는 hack.er라는 이름의 사이트를 기준으로 한다. 코드 -
[6] 모르는 분들을 위하여 설명해 주자면 트윗을 보면 강제로 이 게시물을 리트윗하고 그걸 알리는 경고창을 뛰우는 코드로, 작동했었다. 이후 패치되어 막혔다. [7] 기존에는 JSFuck이 사용되었지만, 제작자의 말로, 오류가 나서 더이상 사용하지 못한다고 한다.[8] 하지만, 사람에 따라서 정규식을 사용하는 것이 개발하기 더 편한 경우가 있다. 하지만, 그렇다고 함부로 대충 정규식을 만들어서 사용했다가 뼛속까지 털리는 수가 있다.[9] 설령 그렇다 하더라도 비표준 태그가 있어서 웹 브라우저마다 호환이 안 될 수도 있다.[10] 주로 <와 >를 대체하여서, [
와 ]
를 사용하여 만든다.[11] 위키백과, 나무위키와 같은 위키들도 모두 BBCode를 사용한다고 할 수 있다.[12] 특히 Let's Encrypt 같은 TLS 인증서 생성 솔루션으로 공격자가 참 쉽게 TLS 환경을 구축할 수 있다.[13] 이 때문에 파이어폭스 등 일부 브라우저는 콘솔에 붙여넣기 시 경고후 확인을 거쳐야 붙여 넣을 수 있다. 또 크롬의 경우 javascript:
로 시작하는 문자열을 붙여넣을 경우 맨 앞의 javascript:
가 빠진다. 그래서 일부 사이트의 경우 스크립트 복사 버튼을 누르면 avascript:
로 시작하는 문자열이 복사되게 설정하고 j자를 직접 입력한 후 붙여넣으라는 설명을 하기도 한다.