CSP nonce는 매 요청마다 새로 생성되는 일회용 토큰으로, 인라인 스크립트가 진짜 서버에서 나온 코드인지 증명하는 XSS 후방어 메커니즘이다.
공격자가 입력값으로 XSS를 주입해도 nonce를 모르면 브라우저가 차단하지만, 정적 nonce·약한 난수·strict-dynamic 누락 같은 잘못된 구현은 후방어를 무력화한다.
이 글은 5가지 실수 패턴, Django 표준 미들웨어 코드, 30초 자가진단을 정리했다.
한눈에 보는 CSP nonce
Q. nonce는 어떻게 XSS를 막나요? A. 브라우저는 CSP 헤더의 nonce 값과 일치하는 script 태그만 실행합니다. 공격자가 주입한 인라인 스크립트는 서버 nonce를 모르기 때문에 차단됩니다. Q. 정적 nonce는 왜 위험한가요? A. 매 요청마다 변경되지 않으면 공격자가 한 번만 nonce를 알아내면 모든 사용자 세션에서 XSS 주입이 가능합니다. 매 응답마다 32바이트 이상 난수로 생성해야 합니다. Q. strict-dynamic은 왜 필요한가요? A. nonce가 부여된 스크립트가 동적으로 로드하는 추가 스크립트도 신뢰 체인으로 자동 허용합니다. 누락 시 React/Vue 같은 SPA가 깨지거나 unsafe-inline 우회 패턴이 생깁니다. Q. report-only 모드로 충분한가요? A. report-only는 차단 없이 위반만 보고합니다. 운영에서는 실제 차단(Content-Security-Policy)으로 전환해야 방어가 동작합니다.5가지 자주 발생하는 실수 패턴
| # | 패턴 | 위험도 |
|---|---|---|
| 1 | nonce를 매 요청 새로 생성하지 않음 (정적 또는 세션당 1회) | 치명 |
| 2 | 난수 엔트로피 부족 (Math.random, 짧은 hex) | 치명 |
| 3 | strict-dynamic 누락 → 동적 스크립트 차단 회피 위해 unsafe-inline 추가 | 치명 |
| 4 | report-only 모드로만 운영, 실제 차단 미적용 | 높음 |
| 5 | script-src에 'self' + https: 광역 허용으로 nonce 무력화 | 높음 |
Django 표준 미들웨어 구현
import secrets
from django.utils.deprecation import MiddlewareMixin
class CSPNonceMiddleware(MiddlewareMixin):
def process_request(self, request):
# 매 요청 새 nonce — 32바이트 = 256bit 엔트로피
request.csp_nonce = secrets.token_urlsafe(32)
def process_response(self, request, response):
nonce = getattr(request, 'csp_nonce', '')
csp = (
f"default-src 'self'; "
f"script-src 'nonce-{nonce}' 'strict-dynamic' https:; "
f"object-src 'none'; base-uri 'self';"
)
response.headers['Content-Security-Policy'] = csp
return response
# 템플릿: <script nonce="{{ request.csp_nonce }}">...</script>
핵심 4요소: (1) 매 요청 새 nonce (2) secrets 모듈 사용 (3) strict-dynamic 포함 (4) object-src 'none'.
hashlib.md5나 random 모듈은 예측 가능해 사용 금지다.
unsafe-inline·unsafe-eval은 nonce 사용 시 무시되지만, 광역 허용은 다른 우회를 만든다.
30초 자가진단
# CSP 헤더 존재 확인
curl -I https://yourdomain.com | grep -i "content-security-policy"
# nonce가 매번 바뀌는지 — 2회 호출 후 nonce 값 비교
for i in 1 2; do curl -s https://yourdomain.com | grep -oE 'nonce="[^"]+"' | head -1; done
nonce 값이 동일하면 정적 nonce 결함이다. OWASP A03: Injection 범주에 해당한다.
24시간 패치 절차
| 순위 | 조치 |
|---|---|
| 1 | secrets.token_urlsafe(32)로 nonce 미들웨어 작성 |
| 2 | script-src에 'nonce-{nonce}' 'strict-dynamic' 적용, unsafe-inline 제거 |
| 3 | 모든 인라인 script 태그에 nonce 속성 부착 |
| 4 | report-uri 또는 report-to로 위반 모니터링 추가 |
| 5 | CSP 보고서 1주일 관찰 후 차단 모드 전환 |
지금 해야 할 것
실무자: CodeScan 무료 스캔으로 CSP + 보안 헤더 자동 점검.
CISO·임원: XSS 후방어 전체 점검은 SENTRIX 30분 1:1 상담.
CSP 도입은 단계적 적용이 필수다.