// ❌ XSS 가능
<script>constdata=${JSON.stringify(data)};</script>// data 안에 </script> 있으면 탈출
// ✅ 별도 element
<scripttype="application/json"id="boot-data">{JSON.stringify(data)}</script>// Client 에서: JSON.parse(document.getElementById('boot-data').textContent)
🤔 의사결정 기준
컨텍스트
인코딩
HTML body 텍스트
escape <>&"'
HTML attribute (title="...")
escape + 따옴표로 감싸기
URL attribute (href, src)
URL encode + 스킴 검증
JS string literal
JSON.stringify + closing tag escape
CSS value
CSS escape
Markdown / Rich text 받을 때
sanitizer (DOMPurify, sanitize-html)
❌ 안티패턴
dangerouslySetInnerHTML 없이 sanitize: 검증 안 된 HTML 직접 주입.
사용자 입력 URL 을 href 그대로: javascript:alert(1) 가능.
eval / Function / new Function: 사용자 입력이 들어가면 RCE.
innerHTML = userInput: 클래식 XSS.
CSP 없이 inline script + nonce 안 씀: 모든 inline 통과.
CSP 의 unsafe-inline / unsafe-eval: CSP 의미 절반 잃음. 마이그레이션 가이드 따라 nonce / hash 로.
서버 검증만 + 클라이언트 출력 인코딩 누락: client-side rendered 변수가 raw HTML. React 디폴트 safe 라 보통 OK 지만 React 외 환경 주의.
이메일/SMS 본문에 사용자 입력 그대로: 다른 종류 인젝션 (SMTP header, link rewriting).
🤖 LLM 활용 힌트
React 외 / SSR template / dangerouslySetInnerHTML / URL 컨텍스트에서 sanitizer 명시.