OPEN HYPER STEP
← 목록으로 (화이트햇 보안)
SECURITY · 80 / 84
security
CHAPTER 80 / 84
읽기 약 2
FUNCTION

XSS: 스크립트 인젝션 방어


핵심 개념

⚠️ 방어 관점. Stored/Reflected/DOM 3가지 XSS 유형 + 출력 인코딩·CSP·HttpOnly 다층 방어.

본문

XSS 3가지 유형

유형저장 위치트리거영향 범위
StoredDB·파일페이지 로드 시 자동모든 방문자
ReflectedURL 파라미터링크 클릭 시링크 클릭한 사용자
DOM-based클라이언트 JS페이지 동작 중특정 동작한 사용자

취약 vs 안전 — 출력 인코딩

PYTHON📋 코드 (45줄)
# ⚠️ 교육 목적, 허가된 환경에서만 사용.
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 response

XSS 취약점 자동 스캐너

PYTHON📋 코드 (40줄)
# ⚠️ 본인 소유 사이트만 점검 — 권한 없는 사이트에 사용 금지.
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