XSS: 브라우저가 데이터를 코드로 해석할 때
XSS는 신뢰하지 않는 데이터가 브라우저의 실행 가능한 문맥에 들어갈 때 생긴다. 출력 위치에 맞는 처리와 안전한 DOM API가 방어의 중심이다.
개념부터 찾아보기
정의·역할·동작 원리를 독립 페이지에서 확인하세요. 본문의 밑줄 용어에도 설명이 있습니다.
이 노트에서 설명할 수 있게 되는 것
- Source에서 Sink까지 비신뢰 데이터의 경로를 추적한다.
- HTML·속성·URL 등 출력 문맥의 차이를 설명한다.
- 안전한 텍스트 삽입과 HTML 정화가 필요한 경우를 구분한다.
01개념과 구성 요소
- 성립 원리신뢰하지 않는 값이 HTML·의 실행 문맥으로 해석되어 페이지 권한으로 동작하는 문제다.
- 추적 대상입력이 들어오는 , 디코딩·정화 과정, DOM이나 코드 실행 를 연결한다.
- 방어 구성텍스트 API·문맥별 출력 처리·HTML 정화·는 서로 다른 경계를 보호한다.
02게시물 문자열이 브라우저 출력이 되는 순서
- 값 획득서버나 브라우저가 요청·저장 게시물·URL·API 응답 등에서 값을 읽는다.
- 출력 문맥 선택값을 HTML 텍스트·속성·URL·스크립트 중 어디에 넣는지 결정한다. 문맥에 따라 필요한 처리가 달라진다.
- 안전한 자료 삽입단순 표시는 textContent로 넣고, 허용된 HTML이 필요하면 정화된 구조를 사용한다.
- 브라우저 해석innerHTML 같은 HTML 는 문자열을 요소 구조로 파싱한다. 실행 가능 여부는 실제 태그·속성·문맥과 정책에 달려 있다.
- 경로 검증응답 원문·최종 DOM·실행 위치를 비교한다. 저장형은 저장 뒤 다른 조회에서도 같은 경로가 이어지는지 확인한다.
03XSS는 브라우저가 데이터를 코드 문맥으로 해석하는 문제다
- 실행 위치에서 주입된 코드가 실행되는 곳은 일반적으로 사용자의 브라우저다. 서버가 위험한 HTML을 만들어 보내거나 클라이언트 가 외부 값을 위험한 DOM 위치에 넣으면, 브라우저는 그 내용을 현재 페이지의 출처 권한으로 처리할 수 있다.
- 입력 출처입력창이 반드시 필요한 것은 아니며 URL·게시물·헤더·파일명·외부 API 결과·관리 로그 화면도 값의 출처가 된다.
- 파서 문맥핵심은 신뢰할 수 없는 값이 최종적으로 어떤 파서와 실행 문맥에 들어가는가다. HTTPS는 전송 중 바이트를 보호하지만 이미 페이지에 포함된 코드가 브라우저에서 수행되는 이 경계를 대신 검사하지 않는다.
- 클라이언트 비밀브라우저에서 실행되는 는 사용자에게 도착한 뒤 개발자 도구로 읽을 수 있으므로 서버용 비밀을 숨겨 두는 저장소가 될 수도 없다.
04Reflected·Stored·DOM-based의 서로 다른 경로
- 반사·저장는 요청값이 즉시 응답 HTML 등에 반영되어 실행되는 형태다. 는 값이 DB나 파일에 저장된 뒤 다른 사용자가 조회할 때 실행된다.
- DOM 분류 축DOM-based 는 브라우저 가 에서 값을 읽어 위험한 로 전달하는 흐름에 초점을 둔다. 저장·반사 여부와 클라이언트 처리 경계는 분류 축이 달라 실제 사례에서 단순 배타적 세 상자로만 나뉘지 않을 수 있다.
- 조회 사용자저장형에서는 처음 입력한 사람과 나중에 위험한 페이지를 읽는 사람이 다를 수 있다. 또한 공개 게시판만이 아니라 운영자가 여는 로그·댓글 관리 화면도 실행 지점이 될 수 있다.
- 클라이언트 제한클라이언트 maxlength를 바꾸었더니 더 긴 값이 저장되었다는 관찰은 브라우저 제한과 서버 검증이 별개라는 사실을 보여 준다.
05Source와 Sink를 코드에서 찾기
- 에는 location.search, location.hash, document.referrer, window.name, localStorage, postMessage 데이터처럼 외부 영향을 받을 수 있는 값이 있다.
- 에는 innerHTML, outerHTML, insertAdjacentHTML, document.write, eval, 문자열을 실행하는 일부 API 등이 있다. 동일한 이름의 값이라도 중간의 디코딩·결합·정화 과정을 따라가야 실제 실행 가능성을 판단할 수 있다.
- URL fragmentURL fragment는 일반적으로 HTTP 요청에 포함되지 않지만 브라우저의 location.hash로 읽을 수 있다. 그래서 서버 로그나 응답 원문에 시험 문자열이 없어도 DOM 경로에서 문제가 생길 수 있다.
- DOM 구조DOM은 외부 객체가 아니라 브라우저가 문서를 해석해 만든 구조이며 가 이 구조를 바꾸는 것이다.
06innerHTML과 textContent의 차이를 눈으로 읽기
- HTML과 텍스트아래 예제에서 같은 <b>학습 표식</b> 값을 innerHTML에 넣으면 HTML 요소로 해석되지만 textContent에 넣으면 꺾쇠를 포함한 텍스트로 표시된다. 이 차이를 이해한 뒤 실행 가능한 문맥을 분석해야 한다.
- Script 실행 조건innerHTML에 넣은 모든 script 태그가 모든 조건에서 즉시 실행된다고 단순화할 수는 없지만 외부 문자열을 HTML로 해석시키는 위험 경계라는 점은 남는다.
- 텍스트 삽입단순 표시 기능이면 문자열을 HTML로 만들 필요가 없다. 요소를 만들고 textContent로 값을 넣으면 데이터가 HTML 구문으로 바뀌는 단계를 제거할 수 있다.
- innerText 차이innerText도 텍스트 성격이지만 렌더링·CSS와의 관계가 달라 textContent와 완전히 같은 API는 아니다.
const value = "<b>학습 표식</b>";
preview.innerHTML = value; // b 요소로 해석
safePreview.textContent = value; // 꺾쇠를 포함한 문자 그대로
const item = document.createElement("li");
item.textContent = userSuppliedLabel;
list.appendChild(item);네트워크 요청이나 정보 유출 없이 HTML 파싱과 텍스트 삽입의 차이를 보여 주는 비교 코드다.
07무해한 표식으로 실행과 저장 시점을 분리하기
- 무해한 실행 표식격리된 학습 페이지에서 <script>alert("training-marker")</script> 같은 무해한 표식은 서버가 만든 응답이 스크립트 문맥으로 실행되는지 확인하는 데 쓰일 수 있다. 표식의 목적은 알림 한 번이 아니라 입력→저장 또는 반사→브라우저 실행의 경로를 확인하는 것이다.
- 관찰 목적실제 쿠키·문서 내용을 외부 수신점으로 보내지 않아도 이 원리를 설명할 수 있다.
- 반사·저장 비교반사형에서는 해당 요청의 응답에서 입력 위치와 주변 태그를 본다. 저장형에서는 새로고침과 다른 가상 사용자 세션에서 같은 내용이 다시 출력되는지 본다.
- DOM과 재디코딩DOM형에서는 응답 원문과 최종 DOM, 실행된 의 · 위치를 비교한다. 서버에서 인코딩했던 값을 클라이언트가 다시 디코딩하여 innerHTML에 넣으면 안전성이 다시 깨질 수 있다.
<script>alert("training-marker")</script>
관찰할 연결:
입력값 → 응답의 삽입 위치 → 파싱된 DOM → 실행 시점
저장형이라면: DB 저장 → 다른 조회 요청 → 같은 위치의 출력수업의 실행 확인 예를 무해한 고정 문자열로 제한했다. 실제 세션이나 외부 전송 코드는 없다.
반사 입력과 저장된 DOM을 서로 다른 경로로 읽기
브라우저 화면·DOM·저장 시점을 구분해 의 와 를 설명한다.
실습 구성과 전제
- 자기 격리 에서 실제 계정 정보가 없는 전용 브라우저를 사용한다.
- 아래 Reflected 페이지와 Stored 페이지는 서로 다른 과제 화면이므로 한 번의 실행 순서로 합치지 않는다.
입력이 어디서 화면으로 돌아오는지 찾는다
- Reflected 과제에서는 폼 입력이 현재 응답에 반영되는 경로를 본다. 먼저 lab-marker 같은 무해한 문자열로 화면에 나타나는 위치를 기록한다.
- 표시됐다는 사실만으로 코드 실행을 확정하지 않는다. HTML 소스와 DOM에서 텍스트인지 요소인지 확인한다.
원본 크기로 보기 ↗- 제목은 Reflected Cross Site Scripting이며 이름 입력칸과 Hello 출력 위치가 보인다.
- 왼쪽에 Stored와 DOM 과제가 별도로 있어 유형이 다른 실습임을 구분할 수 있다.
확인 기준현재 요청과 응답에서 입력이 반영되는 위치를 특정한다.
저장 폼과 DOM의 실행 문맥을 확인한다
- Stored 과제에서는 입력이 저장된 뒤 다른 조회에서도 다시 렌더링되는지 본다. 재방문에 나타나는 것과 스크립트로 실행되는 것은 별도로 기록한다.
- textarea의 maxlength 같은 브라우저 조건은 서버 을 대신하지 않는다. 최종 가 HTML·속성·스크립트 중 무엇인지 결정해야 한다.
원본 크기로 보기 ↗- DOM에 Stored Cross Site Scripting 제목과 POST guestform이 표시된다.
- mtxMessage textarea의 maxlength가 1000으로 보인다. 길이 제한이 있어도 출력 문맥 처리가 필요하다.
확인 기준저장 여부·출력 위치·실행 여부를 세 항목으로 나눈다.
텍스트 출력으로 같은 입력을 비교한다
- 허용된 HTML이 필요하지 않은 영역은 textContent나 문맥에 맞는 을 사용한다. HTML을 허용해야 한다면 정화 정책을 별도로 설계한다.
- 문자열 '<b>lab</b>'를 예로 들어 요소로 렌더링하는 출력과 그대로 글자로 표시하는 출력을 비교한다.
const value = '<b>lab</b>'; output.textContent = value;교육용 DOM 비교 예시. 입력을 코드로 실행하지 않고 글자로 표시한다.
확인 기준수정 후에는 정상 글자가 사라지지 않으면서 데이터가 실행 문맥을 만들지 않아야 한다.
실습에서 확인한 동작 원리
- 반사·저장·DOM 경로는 입력의 이동 방식과 실행 시점을 나누는 기준이다.
- 실제 방어는 최종 출력 문맥에 맞춰 적용한다.
설정·출력 추가로 확인하기 20장
아래 화면을 펼쳐 위 실습의 설정값, 명령과 확인 기준을 비교해 보세요. 각 묶음은 별도 실습 기록입니다.
실습 화면 묶음 01 1장
원본 크기로 보기 ↗- 「CSRF」 문맥에 삽입된 원본 실습·개념 참고 화면이다. XSS 학습 내용과 함께 당시 구성·설정·관찰 결과를 비교할 수 있다.
실습 화면 묶음 02 1장
원본 크기로 보기 ↗- 「실습」 문맥에 삽입된 원본 실습·개념 참고 화면이다. XSS 학습 내용과 함께 당시 구성·설정·관찰 결과를 비교할 수 있다.
실습 화면 묶음 03 4장
원본 크기로 보기 ↗- 「stored」 문맥에 삽입된 원본 구성·동작 참고 화면이다. XSS 학습 내용과 함께 당시 구성·설정·관찰 결과를 비교할 수 있다.
원본 크기로 보기 ↗- 「reclected」 문맥에 삽입된 원본 실습·개념 참고 화면이다. XSS 학습 내용과 함께 당시 구성·설정·관찰 결과를 비교할 수 있다.
원본 크기로 보기 ↗- 「stored」 문맥에 삽입된 원본 실습·개념 참고 화면이다. XSS 학습 내용과 함께 당시 구성·설정·관찰 결과를 비교할 수 있다.
원본 크기로 보기 ↗- 「stored」 문맥에 삽입된 원본 실습·개념 참고 화면이다. XSS 학습 내용과 함께 당시 구성·설정·관찰 결과를 비교할 수 있다.
실습 화면 묶음 04 1장
원본 크기로 보기 ↗- 학습 원본 · XSS 입력과 브라우저·패킷 관찰의 해당 절에 수록된 화면·도식이다.
실습 화면 묶음 05 1장
원본 크기로 보기 ↗- 학습 원본 · XSS 입력과 브라우저·패킷 관찰의 해당 절에 수록된 화면·도식이다.
실습 화면 묶음 06 1장
원본 크기로 보기 ↗- 학습 원본 · XSS 입력과 브라우저·패킷 관찰의 해당 절에 수록된 화면·도식이다.
실습 화면 묶음 07 1장
원본 크기로 보기 ↗- 학습 원본 · XSS 입력과 브라우저·패킷 관찰의 해당 절에 수록된 화면·도식이다.
실습 화면 묶음 08 1장
원본 크기로 보기 ↗- 학습 원본 · XSS 입력과 브라우저·패킷 관찰의 해당 절에 수록된 화면·도식이다.
실습 화면 묶음 09 1장
원본 크기로 보기 ↗- 학습 원본 · XSS 입력과 브라우저·패킷 관찰의 해당 절에 수록된 화면·도식이다.
실습 화면 묶음 10 1장
원본 크기로 보기 ↗- 학습 원본 · XSS 입력과 브라우저·패킷 관찰의 해당 절에 수록된 화면·도식이다.
실습 화면 묶음 11 1장
원본 크기로 보기 ↗- 학습 원본 · XSS 입력과 브라우저·패킷 관찰의 해당 절에 수록된 화면·도식이다.
실습 화면 묶음 12 1장
원본 크기로 보기 ↗- 학습 원본 · XSS 입력과 브라우저·패킷 관찰의 해당 절에 수록된 화면·도식이다.
실습 화면 묶음 13 1장
원본 크기로 보기 ↗- 학습 원본 · XSS 입력과 브라우저·패킷 관찰의 해당 절에 수록된 화면·도식이다.
실습 화면 묶음 14 1장
원본 크기로 보기 ↗- 학습 원본 · XSS 입력과 브라우저·패킷 관찰의 해당 절에 수록된 화면·도식이다.
실습 화면 묶음 15 1장
원본 크기로 보기 ↗- 학습 원본 · XSS 입력과 브라우저·패킷 관찰의 해당 절에 수록된 화면·도식이다.
실습 화면 묶음 16 1장
원본 크기로 보기 ↗- 학습 원본 · XSS 입력과 브라우저·패킷 관찰의 해당 절에 수록된 화면·도식이다.
실습 화면 묶음 17 1장
원본 크기로 보기 ↗- 학습 원본 · XSS 입력과 브라우저·패킷 관찰의 해당 절에 수록된 화면·도식이다.
08출력 인코딩은 문맥에 맞아야 한다
- 출력 문맥HTML 텍스트에 들어갈 값, 따옴표로 둘러싼 속성값, URL, 문자열은 같은 파서 규칙을 쓰지 않는다. HTML 특수문자 처리만 적용한 값을 script 내부에 삽입하거나 URL 검증 없이 링크 주소에 쓰면 기대와 다른 해석이 생길 수 있다.
- 공통 치환의 한계모든 곳에 한 번의 문자열 치환을 적용하는 방식보다 위험한 문맥 자체를 줄이는 것이 중요하다.
- 자동 이스케이프템플릿 엔진의 자동 이스케이프도 어떤 출력 문맥을 보호하는지 범위가 있다. 개발자가 safe 처리나 raw HTML 기능으로 우회하면 기본 보호가 사라질 수 있다.
- 반복 변환인코딩한 값을 다시 인코딩해서 화면이 깨지는 문제와 디코딩을 반복해서 위험 구문이 살아나는 문제도 있으므로 데이터가 어느 형식으로 저장되고 어느 단계에서 변환되는지 명확해야 한다.
09중첩된 출력 문맥과 브라우저 파서
- 파서의 순서HTML 속성 안의 URL은 HTML 해석과 URL 해석을 함께 거친다. 같은 문자열이라도 어느 파서의 입력인지에 따라 필요한 처리가 다르다.
- 안전한 위치단순 텍스트를 표시할 때는 textContent 같은 텍스트용 API를 사용한다. 인코딩된 문자열을 다시 innerHTML에 넣으면 의미가 달라질 수 있다.
- URL 정책링크는 허용 프로토콜·대상 정책을 먼저 적용하고 URL 구성 요소를 인코딩한다. 속성 문자열을 직접 만들면 HTML 속성 문맥도 별도로 처리한다.
- 정화의 경계HTML 기능이 필요하면 허용 요소·속성·URL 규칙으로 정화한다. 정화 후 DOM을 위험한 방식으로 다시 조립하면 보장 범위가 깨진다.
- 보조 통제는 실행 출처와 형태를 제한하는 추가 경계다. 허용된 스크립트가 위험한 에 값을 넣는 문제까지 자동으로 없애지는 않는다.
10가상 제목 A&B <C>가 표시되는 과정
- 요구 정의사용자가 입력한 제목은 A&B <C>라는 텍스트이며 태그나 엔터티 기능을 의도하지 않았다고 가정한다.
- 텍스트 APIdiv 같은 일반 요소의 textContent에 원래 값을 넣으면 텍스트 노드가 되어 <C>가 HTML 요소로 파싱되지 않는다. script·style 요소에 신뢰할 수 없는 내용을 넣는 경우에 이 설명을 적용하면 안 된다.
- HTML 문자열서버가 HTML 본문 문자열을 생성하는 경우 A&B <C>처럼 해당 문맥에 맞게 표현한다.
- 파싱 결과HTML 파서는 엔터티를 문자로 해석하여 화면에 A&B <C>를 보여 준다. 엔터티가 풀렸다고 입력을 다시 HTML 코드로 실행하는 것은 아니다.
- 이중 처리이미 인코딩된 &를 다시 인코딩하면 &amp;가 되어 화면에 &가 남을 수 있다. 저장 형식과 출력 변환의 책임을 구분한다.
11응답에는 없는데 DOM에서 나타나는 입력
- 입력 출처URL fragment·브라우저 저장소·메시지 이벤트처럼 서버 응답에 직접 나타나지 않는 를 추적한다.
- 값의 변형디코딩·문자열 연결·템플릿 렌더링을 거쳐 어느 에 도달했는지 본다. 최종 화면만 보아서는 최초 입력 경로를 알기 어렵다.
- 문맥 변화HTML 텍스트용 인코딩을 ·CSS 문자열에도 공통 적용하지 않는다. 코드 문맥에 외부 값을 삽입하는 설계부터 줄인다.
- 관찰 조건저장형 문제는 작성자 화면과 열람자 화면이 다를 수 있다. 실행 여부·권한·페이지·시점을 분리하여 재현 결과를 기록한다.
12HTML이 필요한 기능은 정화 규칙이 필요하다
- 허용 HTML서식 있는 게시물을 허용해야 한다면 모든 태그를 일반 텍스트로 바꾸는 것과 허용된 HTML을 정화하는 것은 다른 기능이다. 정화기는 허용할 요소·속성·URL 스킴 등을 구조적으로 해석하고 위험한 구성을 제거한다.
- 정화기의 역할으로 script라는 단어 하나를 지우는 방식은 브라우저가 가진 다양한 파싱 문맥을 대체하지 못한다.
- 변환 후 안전성정화 뒤 문자열을 다시 결합하거나 다른 파서 문맥으로 이동하면 앞선 보장이 달라질 수 있다. 그래서 저장 시 검증뿐 아니라 최종 출력 경계의 처리 방식이 중요하다.
- 기존 저장 자료기존 저장 자료에 위험한 값이 남아 있다면 코드 수정 후에도 조회 경로가 이를 안전하게 처리해야 하며, 새 글 입력만 차단했다고 저장형 위험이 모두 사라진 것은 아니다.
13CSP와 쿠키 속성은 피해 경로를 제한한다
- 는 브라우저가 어떤 스크립트와 자원을 실행·불러올 수 있는지 제한한다. nonce나 해시를 사용하는 스크립트 정책은 허용된 코드와 주입된 코드를 구분하는 데 쓰일 수 있다.
- 실제 차단 모드단, 보고 전용 정책과 실제 차단 정책은 결과가 다르며 가 안전한 출력 처리를 대신하지 않는다. 허용된 외부 스크립트 자체가 변조되는 공급망 문제도 별도로 남는다.
- 쿠키 속성HttpOnly는 일반적인 쿠키 읽기를 제한하지만 코드가 같은 출처에서 사용자의 권한으로 요청을 만드는 경로까지 없애지 않는다. Secure는 전송 조건을, SameSite는 교차 사이트 쿠키 전송을 다룬다.
- 다층 통제각 속성이 보호하는 지점이 달라 한 속성이 켜져 있다는 이유로 가 성립하지 않는다고 판단하지 않는다.
14iframe 표시와 DOM 접근은 다른 정책이다
- 동일 출처 정책다른 출처 iframe이 화면에 보인다고 부모 페이지가 그 내부 DOM과 입력값을 자유롭게 읽는 것은 아니다. Same-Origin Policy는 스킴·호스트·포트로 구분한 출처 사이의 접근을 제한한다.
- Mixed ContentHTTPS 페이지에서 HTTP iframe을 불러오는 Mixed Content 문제는 전송 보안의 문제이며, 모두 HTTPS라고 DOM 접근 제한이 사라지는 것도 아니다.
- 프레임 포함 정책X-Frame-Options와 frame-ancestors는 누가 내 페이지를 프레임으로 포함할 수 있는지 제한해 클릭재킹 방어와 연결된다. 반대로 frame-src는 내가 어떤 프레임을 불러올 수 있는지를 제어한다.
- Sandboxiframe sandbox는 내부 기능 권한을 제한한다. 세 기술의 방향을 구분해야 “iframe을 막는다”는 막연한 설명을 실제 응답 헤더와 연결할 수 있다.
15외부 자바스크립트와 SRI의 신뢰 경계
- 외부 스크립트 권한CDN에서 내려받은 는 다른 서버에서 왔더라도 페이지가 포함하여 실행하면 그 페이지의 권한 문맥에서 동작할 수 있다. HTTPS는 다운로드 중 변조를 막는 경로를 제공하지만 신뢰한 배포 서버나 라이브러리의 잘못된 변경까지 자동으로 판별하지 않는다.
- SRISRI는 개발자가 지정한 해시와 내려받은 자원의 바이트를 비교하는 방식이다.
- 통제 역할 분리해시가 고정되어 있으면 파일이 바뀌었을 때 실행되지 않을 수 있으므로 버전 관리와 배포 동작을 함께 이해해야 한다. 의 출처 제한, SRI의 내용 무결성, 애플리케이션의 은 각기 다른 통제다.
- 네트워크 방화벽 한계네트워크 방화벽은 브라우저 DOM의 출력 문맥을 안전하게 만드는 기능을 대신하지 않는다.
설명과 함께 보는 보조 도식
모식도 · DOM 데이터에서 실행 경계까지
안전한 텍스트와 해석 가능한 HTML의 경계를 비교한다.
입력 수신
출처가 외부인 데이터의 경로를 표시한다.
전체 단계를 한 번에 읽기
- 01
입력 수신
출처가 외부인 데이터의 경로를 표시한다.
- 02
정책 선택
단순 텍스트인지 허용 서식인지 결정한다.
- 03
안전한 표시
텍스트 필드는 텍스트 Sink로 보낸다.
- 결과
- 문자 데이터
- 04
HTML 처리 확인
HTML이 필요하면 정화 정책과 최종 문맥을 함께 검증한다.
- 증거
- 최종 DOM과 브라우저 동작
함께 보는 학습 자료
아래 공식 자료를 본문의 개념·예제와 함께 읽어 보세요. 자료별로 확인할 위치와 관찰할 내용을 적었습니다.
OWASP: HTML·속성·DOM 출력 문맥 비교
같은 입력도 삽입되는 파서 문맥에 따라 방어가 달라진다는 점을 예제별로 비교할 수 있다.
- 일반 요소 textContent와 HTML 문자열 인코딩의 차이
- 따옴표 있는 속성과 이벤트 처리기 속성의 차이
- HTML·URL· 문맥에 필요한 별도 통제
이해했는지 확인하기
- 외부 데이터가 들어오는 Source와 출력 Sink를 연결한다.
- 출력 문맥에 맞는 인코딩·정화·안전 API를 확인한다.
- HttpOnly와 CSP가 근본 수정의 대체물로 쓰이지 않는지 확인한다.