교차 사이트 스크립팅
남이 넣은 코드가 그 사이트 자신의 코드인 것처럼 브라우저에서 실행되는 실패입니다. 사이트가 바깥에서 받아온 값을 페이지에 그대로 찍을 때 생깁니다. 브라우저는 도착한 코드가 믿을 만한지 가려낼 근거가 없어서 그냥 실행합니다.
상세
XSS(Cross-Site Scripting, 교차 사이트 스크립팅)는 값이 데이터로 남지 못하고 코드가 되어 버리는 실패입니다. MITRE 의 CWE(Common Weakness Enumeration) 79번 항목은 이것을 이렇게 적습니다. 제품이 다른 사용자에게 제공되는 웹 페이지로 쓰이는 출력에 사용자가 통제할 수 있는 입력을 넣기 전에, 그 입력을 중화하지 않거나 잘못 중화한다는 것입니다.
CWE-79 는 변종이 많다고 덧붙입니다. 부르는 말도 여럿이고 공격 형태도 여럿입니다. 그런데 그것들이 전부 같은 근본 약점을 가리킨다고 적습니다. 공격자와 피해자 사이에 놓인 위험한 입력을 제대로 중화하지 않은 것입니다.
OWASP(Open Worldwide Application Security Project) 는 같은 것을 공격 쪽에서 적습니다. XSS 공격은 인젝션의 한 종류이고, 원래는 무해하고 신뢰받는 웹사이트에 악성 스크립트가 주입되는 것이라는 설명입니다. 공격자는 이것으로 아무것도 모르는 사용자에게 악성 스크립트를 보낼 수 있습니다. 최종 사용자의 브라우저는 그 스크립트를 믿으면 안 된다는 것을 알 방법이 없고, 그래서 실행합니다.
무엇이 뒤집히는지는 MDN(Mozilla Developer Network) 이 짚습니다. 브라우저는 여러 사이트에서 코드를 내려받아 사용자의 컴퓨터에서 돌립니다. 그중에는 사용자가 금융 거래나 의료 상담 같은 민감한 일에 쓰는 사이트도 있고, 그런 신뢰 관계가 전혀 없는 사이트도 있습니다. 브라우저 보안 모델의 토대는 이 사이트들이 서로 분리된 채로 있어야 한다는 것입니다. 한 사이트에서 온 코드가 다른 사이트의 객체나 크리덴셜에 접근하지 못해야 하고, 이것을 동일 출처 정책이라고 부릅니다.
XSS 공격이 성공하면 공격자는 그 정책을 뒤집습니다. MDN 은 공격자가 대상 사이트를 속여 악성 코드를 그 사이트 자신의 문맥 안에서 마치 동일 출처인 것처럼 실행하게 만든다고 적습니다. 그러면 그 코드는 사이트 자신의 코드가 할 수 있는 것을 할 수 있습니다. MDN 이 드는 예는 둘입니다. 사이트가 띄운 페이지의 모든 내용과 로컬 저장소의 내용을 읽거나 고치는 것, 그리고 사용자의 크리덴셜을 실어 HTTP(HyperText Transfer Protocol) 요청을 보내 사용자를 사칭하거나 민감한 데이터에 접근하는 것입니다.
CWE-79 의 배경 설명도 같은 자리를 짚습니다. 동일 출처 정책은 브라우저가 어떤 웹사이트, 곧 출처에서 도는 스크립트가 접근할 수 있는 리소스를 그 웹사이트에 딸린 클라이언트 쪽 리소스로 한정해야 한다고 정합니다. 다른 사이트, 다른 출처의 클라이언트 쪽 리소스는 안 됩니다. 목적은 한 사이트가 무관한 사이트의 내용을 고치거나 읽지 못하게 막는 것입니다.
발생 조건
CWE-79 의 정의 한 문장이 조건 셋을 담고 있습니다. 셋이 동시에 성립할 때 터집니다.
- 페이지에 들어갈 데이터가 사용자가 통제할 수 있는 곳에서 온다
- 그 데이터가 다른 사용자에게 제공되는 웹 페이지의 출력에 들어간다
- 들어가기 전에 중화가 없거나 잘못돼 있다
flowchart TD
A[사용자가 통제할 수 있는 입력] --> B{페이지 출력에 들어가나}
B -->|안 들어간다| S[데이터로 남는다]
B -->|들어간다| C{출력 문맥에 맞는 중화를 거치나}
C -->|거친다| S
C -->|안 거친다| D[브라우저가 마크업이나 스크립트로 해석한다]
D --> E[취약한 사이트의 출처 권한으로 실행된다]
셋 중 하나만 빠져도 실행까지 가지 않습니다.
조건 1 을 빼면
페이지에 박히는 값이 전부 코드 안에 고정돼 있으면 밀어 넣을 자리가 없습니다. 반사형 재현이 요청 파라미터를 방아쇠로 삼고 DOM(Document Object Model) 기반 재현이 현재 주소를 방아쇠로 삼는 것은, 둘 다 그 자리가 바깥에서 값을 받는 통로이기 때문입니다.
조건 2 를 빼면
값이 사용자 손에서 왔어도 다른 사용자에게 제공되는 페이지의 출력에 안 들어가면 브라우저가 그것을 해석할 자리가 없습니다. 서버 안에만 쌓이고 마는 값이 그렇습니다.
이 조건이 어느 경로로 성립하느냐가 갈래를 가르는 자리입니다. 저장형은 값이 서버에 저장됐다가 피해자가 꺼내 갈 때 출력에 들어갑니다. 반사형은 저장을 거치지 않고 요청에 담긴 값이 곧바로 응답에 실립니다. DOM 기반은 서버가 보낸 응답에 페이로드가 없고, 클라이언트 쪽 코드가 값을 읽어 DOM 에 쓰는 순간 출력이 됩니다.
조건 3 을 빼면
중화가 들어가면 같은 입력이 들어와도 글자로 남습니다. 다만 무엇을 중화해야 하는지는
값이 놓이는 자리마다 다릅니다. OWASP 의 Cross Site Scripting Prevention Cheat Sheet 는
변수가 놓이는 출력 문맥을 나눠서 다룹니다. <div> 나 <b> 같은 기본
HTML(HyperText Markup Language) 태그 사이에 변수를 끼우는 HTML 문맥, 변수를 HTML 속성
값 자리에 놓는 HTML 속성 문맥, 인라인 JavaScript 에 넣고 그것을 HTML 문서에 박는
JavaScript 문맥, 인라인 CSS(Cascading Style Sheets) 에 넣는 CSS 문맥,
URL(Uniform Resource Locator) 에 넣는 URL 문맥입니다.
같은 문서는 이 문맥들과 별도로 위험한 문맥을 따로 구분해 다룹니다. <script> 태그 안,
HTML 주석 안, CSS 셀렉터 안처럼 출력 인코딩만으로는 안전해지지 않는 자리입니다.
그래서 어느 문맥에 놓였는지를 빼고 "이스케이프했다" 고만 말하면 조건 3 이 아직 서 있는지
아닌지를 판정할 수 없습니다.
예시
CWE-79 의 반사형 예제
CWE-79 는 HTTP GET 의 username 파라미터로 환영 문구를 찍는 코드를 취약 코드로 싣습니다.
반사형 시나리오를 다루는 예제이고, PHP 로 쓰였습니다.
$username = $_GET['username'];
echo '<div class="header"> Welcome, ' . $username . '</div>';
파라미터에는 무엇이든 올 수 있으므로, $username 이 스크립트 구문을 담도록 페이지의
URL 을 고칠 수 있습니다.
http://trustedSite.example.com/welcome.php?username=<Script Language="Javascript">alert("You've been attacked!");</Script>
CWE-79 는 이것이 무해한 경고창 하나를 띄우는 데 그친다고 적습니다. 그리고 처음에는 이게 그리 대단한 취약점처럼 안 보일 수 있다고 덧붙입니다. 자기 컴퓨터에서 악성 코드가 돌게 만드는 URL 을 누가 직접 넣겠느냐는 것입니다. 진짜 위험은 공격자가 그 악성 URL 을 만들어 두고 메일이나 사회공학 수법으로 피해자를 그 링크로 유인하는 데 있습니다. 피해자가 링크를 누르면, 자기도 모르게 악성 콘텐츠를 취약한 웹 애플리케이션을 거쳐 자기 컴퓨터로 되돌려 받습니다.
OWASP 가 싣는 DOM 기반 재현
성격이 다른 둘째입니다. OWASP 의 DOM 기반 XSS 문서는 선택 항목을 찍는 코드를 안전하지 않은 예로 싣습니다.
document.write("<OPTION value=1>"+decodeURIComponent(document.location.href.substring(document.location.href.indexOf("default=")+8))+"</OPTION>");
값이 서버에서 오지 않습니다. 현재 주소에서 default= 뒤를 잘라 그대로 DOM 에 씁니다.
같은 문서는 페이지 자체, 그러니까 HTTP 응답은 바뀌지 않고 페이지에 든 클라이언트 쪽
코드가 다르게 실행되는 것이라고 적습니다. 그리고 서버가 보낸 HTTP 응답에는 공격자의
페이로드가 들어 있지 않다고 못 박습니다.
jQuery 3.4.1 이하의 htmlPrefilter
제품과 판이 붙은 사례입니다. jQuery 는 메서드에 넘어온 닫는 태그를 전부
XHTML(Extensible HyperText Markup Language) 규격에 맞추려고 jQuery.htmlPrefilter 에서
정규식을 썼습니다. jQuery 공식 공지는 그 정규식이 XSS 취약점을 낼 수 있다는 것을 보인
제보가 최근 있었다고 적습니다. 3.4.1 이하의 HTML
파서는 대개 의도대로 동작했지만, 파싱이 의도치 않은 결과를 내는 경계 사례가 있었다는
것입니다. 3.5.0 의 jQuery.htmlPrefilter 는 정규식을 쓰지 않고 문자열을 그대로
통과시킵니다.
이 취약점에는 CVE(Common Vulnerabilities and Exposures) 번호 CVE-2020-11022 가 붙었습니다.
공개된 자문은 신뢰할 수 없는 곳에서 온 HTML 을, 새니타이즈를 거친 뒤라 하더라도
.html() · .append() 같은 jQuery 의 DOM 조작 메서드에 넘기면 신뢰할 수 없는 코드가
실행될 수 있다고 적습니다. 영향 범위는 jQuery 1.12.0 부터 3.4.x 까지이고, 2020년 4월에
나온 3.5.0 에서 해소됐습니다.
갈래
신뢰할 수 없는 데이터가 어디를 거쳐 실행 지점에 닿느냐가 축입니다. OWASP 는 이 축 위에서 셋을 가릅니다.
저장형
사용자 입력이 대상 서버에 저장될 때 흔히 나타납니다. 데이터베이스, 게시판, 방문 기록, 댓글 칸 같은 자리입니다. 그리고 피해자가 그 저장된 데이터를 웹 애플리케이션에서 꺼내 가는데, 그 데이터가 브라우저에서 렌더링해도 안전한 상태로 만들어지지 않은 경우입니다.
sequenceDiagram
participant 공격자
participant 서버
participant 저장소
participant 피해자
공격자->>서버: 스크립트가 섞인 입력
서버->>저장소: 그대로 저장
피해자->>서버: 페이지 요청
서버-->>피해자: 저장된 값이 박힌 응답
Note over 피해자: 브라우저가 실행한다
반사형
사용자 입력이 오류 메시지나 검색 결과처럼 요청에 담긴 입력의 일부나 전부를 포함하는 응답으로 즉시 되돌아올 때입니다. 그 데이터가 브라우저에서 렌더링해도 안전한 상태로 만들어지지 않고, 사용자가 넣은 데이터를 영구히 저장하지도 않습니다. 저장형과 가르는 자리가 이 영구 저장 여부입니다.
sequenceDiagram
participant 공격자
participant 피해자
participant 서버
공격자->>피해자: 입력이 담긴 링크를 보낸다
피해자->>서버: 그 링크로 요청
서버-->>피해자: 입력을 그대로 실은 응답
Note over 피해자: 브라우저가 실행한다
DOM 기반
일부 문헌에서 type-0 XSS 라고도 부릅니다. 원래의 클라이언트 쪽 스크립트가 쓰는 피해자 브라우저의 DOM 환경이 수정된 결과로 공격 페이로드가 실행되는 XSS 공격입니다. 그래서 클라이언트 쪽 코드가 예상 밖의 방식으로 돕니다.
앞의 둘과 가르는 자리는 서버 응답입니다. 페이지 자체는 바뀌지 않고, 서버가 보낸 HTTP 응답에는 공격자의 페이로드가 들어 있지 않습니다.
sequenceDiagram
participant 공격자
participant 피해자
participant 서버
공격자->>피해자: 페이로드가 담긴 주소를 보낸다
피해자->>서버: 페이지 요청
서버-->>피해자: 페이로드가 없는 응답
Note over 피해자: 클라이언트 코드가 주소를 읽어 DOM 에 쓴다
관련 항목
값을 안전하게 만드는 개념·수단
출력 인코딩 · 이스케이프 · 새니타이저 · HTML 인코딩 · 출력 문맥
브라우저가 지원하는 방어 기능
동일 출처 정책 · 콘텐츠 보안 정책(CSP) · Trusted Types · HttpOnly
상위·형제·이웃 공격
인젝션 · SQL 인젝션 · 크로스 사이트 요청 위조(CSRF)
취약점과 방어를 다루는 기관·문서
MITRE · OWASP · W3C(World Wide Web Consortium) · MDN
그 기관이 매기는 분류 번호
공격이 오가는 실행 환경
출력 문맥마다 다르게 쓰이는 언어
HTML · 태그 · CSS · JavaScript · URL
예시가 보여주는 기술·수법
GET · PHP · XHTML · jQuery · 사회공학
DOM 위의 위험한 삽입 지점
DOM · document.write · innerHTML
값이 저장되거나 실려 오가는 위치
공격이 훔치거나 건드리는 대상
다른 이름: Cross-Site Scripting · XSS · 크로스 사이트 스크립팅 · type-0 XSS