XSS와 안전한 출력 처리
저장·반사·DOM 기반 XSS와 출력 문맥별 방어를 구분합니다.
1. XSS가 성립하는 조건
XSS는 보통 다음 조건이 연결될 때 발생한다.
공격자가 영향을 줄 수 있는 값
↓
서버 응답 또는 Client JavaScript가 값을 읽음
↓
값이 HTML·속성·Script 등 실행 가능한 문맥에 안전하지 않게 삽입됨
↓
피해자의 Browser가 이를 코드로 해석
공격 결과는 다음과 같다.
- 사용자 대신 상태 변경 요청 수행
- 화면·입력 폼 변조와 피싱
- 민감정보 노출
- 권한 있는 사용자의 기능 악용
- 악성 사이트 이동 또는 추가 스크립트 실행
HttpOnly가 적용된 세션 쿠키는 JavaScript로 직접 읽기 어렵지만, 공격 스크립트가 피해자의 브라우저에서 인증된 요청을 보내는 것까지 막는 것은 아니다.
2. Stored·Reflected·DOM-based XSS
| 구분 | 위험한 값의 경로 | 실행 시점 | 대표 예 |
|---|---|---|---|
| Stored XSS | 입력값이 DB·게시물·프로필 등에 저장된 뒤 응답에 포함 | 해당 데이터를 보는 사용자가 페이지를 열 때 | 게시글 본문, 관리자 화면의 문의 내용 |
| Reflected XSS | 요청값이 같은 요청의 응답에 즉시 반영 | 조작된 링크나 요청을 피해자가 열 때 | 검색어·오류 메시지·URL 매개변수 |
| DOM-based XSS | 클라이언트 JavaScript가 소스의 값을 위험한 Sink에 전달 | 브라우저 안에서 DOM을 갱신할 때 | location 값이 innerHTML로 전달 |
Stored와 Reflected는 주로 값이 서버 응답에 포함되는 경로를 설명한다. DOM-based는 브라우저 안의 소스-to-Sink 흐름을 강조한다. 한 취약점이 두 분류에 동시에 해당할 수도 있다.
3. 출력 인코딩은 값을 현재 문맥의 데이터로 고정한다
출력 인코딩은 브라우저가 외부 값을 태그·속성·스크립트가 아니라 단순 데이터로 해석하도록 변환하는 방어다. 같은 문자열이라도 삽입 위치가 다르면 필요한 처리가 달라진다.
| 출력 문맥 | 기본 방어 | 주의점 |
|---|---|---|
| HTML 본문 Text | HTML Entity Encoding 또는 textContent | innerHTML로 넣지 않음 |
| HTML 속성값 | 속성값 인코딩, 따옴표 사용, 허용 속성 제한 | Event Handler 속성은 외부 값 삽입 금지 |
| URL | 허용 Scheme·Host 검증 후 URL Encoding | javascript: 등 위험 Scheme 차단 |
| JavaScript | 외부 값을 스크립트 코드 안에 직접 연결하지 않음 | JSON 직렬화와 안전한 DOM API 사용 |
| CSS | 외부 값을 CSS 코드에 직접 삽입하지 않음 | 허용 가능한 값만 선택형으로 제한 |
HTML 본문에서 안전한 인코딩을 했다는 사실이 JavaScript 문자열이나 URL 문맥까지 자동으로 보호하는 것은 아니다.
4. Safe Sink를 우선 사용한다
외부 값을 코드로 해석할 수 있는 Sink보다 데이터 전용 API를 선택하면 실수를 줄일 수 있다.
권장: textContent, createTextNode, 안전한 Attribute API
주의: innerHTML, outerHTML, document.write, eval, 문자열 Event Handler
프레임워크의 기본 Auto-Escaping은 유용하지만 다음 기능에서는 보호가 우회될 수 있다.
- Raw HTML 삽입 기능
- 직접 DOM 조작
- 외부 Template 또는 Plugin
- URL·JavaScript·CSS처럼 기본 HTML 인코딩과 다른 문맥
따라서 “프레임워크를 사용하므로 XSS가 없다”가 아니라, Escape 기능을 해제하는 지점과 위험한 Sink를 확인해야 한다.
5. HTML Sanitization이 필요한 경우
게시판 편집기처럼 사용자가 일부 HTML을 작성해야 하는 기능에서는 모든 태그를 Text로 바꾸면 기능이 사라진다. 이때는 검증된 Sanitizer로 허용 태그·속성·URL Scheme만 남긴다.
출력 인코딩: HTML 자체를 허용하지 않고 값을 Text로 표시
Sanitization: 제한된 HTML 기능은 유지하되 위험 요소를 제거
Sanitization 대상에는 다음이 포함될 수 있다.
script, Event Handler 속성- 위험한 URL Scheme
- 외부 Resource 또는 예상하지 않은 Embed
- 브라우저 해석 차이를 악용하는 비정상 Markup
Sanitization 후 다시 문자열을 결합하거나 위험한 DOM API로 변형하면 보호가 무효화될 수 있다.
6. SQL 삽입·XSS·CSRF를 구분한다
| 취약점 | 코드가 해석되는 주체 | 핵심 원인 | 대표 대응 |
|---|---|---|---|
| SQL Injection | DBMS | 입력이 SQL 구조와 결합 | Parameterized Query |
| XSS | 피해자 브라우저 | 값이 실행 가능한 출력 문맥에 삽입 | 문맥별 출력 처리·Safe Sink |
| CSRF | 대상 Web 서버가 정상 요청으로 오인 | 인증정보 자동 포함과 의도 검증 부재 | CSRF 토큰·SameSite·Origin 검증 |
XSS가 존재하면 공격자가 정상 페이지의 스크립트 문맥을 사용할 수 있으므로 많은 CSRF 방어를 우회할 수 있다. 두 취약점의 원인은 다르지만 함께 관리해야 한다.
개념 확인 문제
문제를 누르면 바로 아래에서 정답과 해설을 확인할 수 있습니다.
01HTML 본문 인코딩을 하면 JavaScript·URL 문맥에도 항상 안전한가?
아니다. 삽입 위치마다 해석 규칙이 다르므로 문맥에 맞는 처리나 안전한 API가 필요하다.
02일부 HTML 편집 기능을 유지하면서 위험 요소를 제거하려면 무엇을 사용하는가?
검증된 HTML Sanitizer로 허용 태그·속성·URL만 남긴다. 일반 텍스트 출력 인코딩과 목적이 다르며 처리 후 위험한 문자열 결합을 하지 않는다.