security
CHAPTER 80 / 84
읽기 약 2분
FUNCTION
XSS: 스크립트 인젝션 방어
핵심 개념
⚠️ 방어 관점. Stored/Reflected/DOM 3가지 XSS 유형 + 출력 인코딩·CSP·HttpOnly 다층 방어.
본문
XSS 3가지 유형
| 유형 | 저장 위치 | 트리거 | 영향 범위 |
|---|---|---|---|
| Stored | DB·파일 | 페이지 로드 시 자동 | 모든 방문자 |
| Reflected | URL 파라미터 | 링크 클릭 시 | 링크 클릭한 사용자 |
| DOM-based | 클라이언트 JS | 페이지 동작 중 | 특정 동작한 사용자 |
취약 vs 안전 — 출력 인코딩
# ⚠️ 교육 목적, 허가된 환경에서만 사용.
from flask import Flask, request, render_template_string
import html
app = Flask(__name__)
# ❌ 취약: 사용자 입력을 HTML에 그대로 삽입
@app.route('/search-vulnerable')
def search_vulnerable():
query = request.args.get('q', '')
return f'<h1>검색 결과: {query}</h1>'
# 공격: ?q=<script>alert(document.cookie)</script>
# ✅ 안전 1: HTML 엔티티 인코딩
@app.route('/search-safe-v1')
def search_safe_v1():
query = request.args.get('q', '')
safe_query = html.escape(query)
return f'<h1>검색 결과: {safe_query}</h1>'
# ✅ 안전 2: 템플릿 엔진 자동 이스케이프 (Jinja2)
@app.route('/search-safe-v2')
def search_safe_v2():
query = request.args.get('q', '')
return render_template_string(
'<h1>검색 결과: {{ query }}</h1>', # Jinja2 자동 이스케이프
query=query,
)
# ✅ 안전 3: Content-Security-Policy 헤더
@app.after_request
def set_csp(response):
response.headers['Content-Security-Policy'] = (
"default-src 'self'; "
"script-src 'self'; "
"style-src 'self' 'unsafe-inline'; "
"img-src 'self' data:;"
)
response.headers['X-XSS-Protection'] = '1; mode=block'
response.headers['X-Content-Type-Options'] = 'nosniff'
return responseXSS 취약점 자동 스캐너
# ⚠️ 본인 소유 사이트만 점검 — 권한 없는 사이트에 사용 금지.
import requests
from bs4 import BeautifulSoup
from urllib.parse import urljoin, urlencode
XSS_PAYLOADS = [
'<script>alert(1)</script>',
'"><script>alert(1)</script>',
"javascript:alert(1)",
'<img src=x onerror=alert(1)>',
'<svg onload=alert(1)>',
]
def scan_xss(url: str, params: dict) -> list[dict]:
findings = []
for payload in XSS_PAYLOADS:
for key in params:
test_params = {**params, key: payload}
try:
r = requests.get(url, params=test_params, timeout=5)
if payload in r.text:
findings.append({
'param': key,
'payload': payload,
'url': r.url,
'evidence': '응답 본문에 payload 그대로 반사됨',
})
except requests.RequestException as e:
print(f'[!] 요청 실패: {e}')
return findings
# 사용 (본인 사이트)
results = scan_xss(
'https://my-site.local/search',
{'q': 'test', 'lang': 'ko'},
)
for f in results:
print(f" 취약: {f['param']} → {f['payload']}")다층 방어 체크리스트
- ✅ 출력 인코딩 (HTML/JS/CSS/URL 컨텍스트별)
- ✅ CSP 헤더 (script-src 'self' 또는 nonce)
- ✅ HttpOnly 쿠키 (JS에서 document.cookie 접근 차단)
- ✅ Secure + SameSite=Strict 쿠키
- ✅ 입력 검증 (블랙리스트가 아닌 화이트리스트)
- ✅ React/Vue 자동 이스케이프 (dangerouslySetInnerHTML 금지)
AI 프롬프트
🤖 AI에게 잘 물어보는 법 — 모델·전략별 프롬프트
Claude
무료: Sonnet 4.6 / Pro $20/mo: Opus 4.6
내 웹앱의 XSS 가능 위치를 보여줄게. 출력 컨텍스트별(HTML/JS/CSS/URL) 적합한 인코딩 방식을 적용해서 리팩터링해줘.
ChatGPT
무료: GPT-5.5 / Plus $20/mo: GPT-5.5 Pro
CSP nonce 기반 정책 설정 가이드를 Next.js·Express·Django 3가지 프레임워크별로 실제 작동하는 코드와 함께 알려줘.
Gemini
무료: 2.5 Flash / Pro $19.99/mo: 3.1 Pro
내 도메인의 모든 페이지를 분석해서 CSP / X-Frame-Options / HttpOnly 등 보안 헤더 누락을 우선순위 매트릭스로 보고해줘.
Grok
무료: Grok 4.1 / SuperGrok $30/mo
2026년 React/Vue 환경에서 XSS 우회 사례 Top 5와 dangerouslySetInnerHTML/v-html 대안을 실전 코드로 솔직히 알려줘.
⭐ 이것만 기억하세요
XSS: 스크립트 인젝션 방어는 이 3가지만 확실히 잡으세요
1.XSS는 백엔드와 프런트엔드 양쪽에서 출력 인코딩이 필요 — 한쪽만 막으면 우회 가능
2.CSP 헤더는 XSS의 마지막 방어선 — 인코딩 누락 시에도 스크립트 실행 자체를 차단
3.다음 챕터에서 CSRF·SSRF·Path Traversal — 사용자 액션·서버 요청·파일 경로 조작 방어
공유하기
진행도 80 / 84