CSP 빌더 완벽 가이드
Content Security Policy(CSP)는 브라우저에 "이 페이지가 신뢰하는 리소스 출처"를 알려 XSS, 클릭재킹, 데이터 인젝션 공격을 방어하는 표준 보안 헤더입니다. 한 줄의 헤더로 인라인 스크립트 차단, 외부 도메인 화이트리스트, iframe 임베드 제어를 동시에 적용할 수 있어 최근 모든 신규 서비스에 사실상 필수로 도입됩니다.
10대 디렉티브
- default-src: 다른 -src 가 명시되지 않은 모든 리소스의 폴백.
- script-src: JS 출처. XSS 방어의 핵심.
- style-src: CSS 출처. 인라인 style은 'unsafe-inline' 또는 nonce/hash 필요.
- img-src: 이미지. data:, blob: 허용 여부도 여기서 결정.
- connect-src: XHR, fetch, WebSocket(wss://), EventSource.
- font-src: 웹폰트. Google Fonts는 https://fonts.gstatic.com.
- object-src: Flash 등 플러그인. 'none' 권장.
- frame-src: iframe 임베드 출처(YouTube, 결제 등).
- base-uri: <base> 태그 변조 방지. 보통 'self'.
- form-action: form action 전송 대상.
주요 소스 표현
'self'(현재 origin), 'none'(모두 거부), 'unsafe-inline'(인라인 허용 — 권장 안 함), 'unsafe-eval'(eval 허용), https:(모든 https 도메인), data:(data URI), blob:(blob URI), https://example.com(특정 호스트), 'nonce-RANDOM'(요청별 임의 토큰), 'sha256-BASE64'(스크립트 해시). nonce/hash 는 'unsafe-inline' 의 대안으로 가장 안전합니다. 'strict-dynamic' 은 nonce 가 부여된 스크립트가 동적으로 로드한 자식 스크립트를 자동 허용해 SPA 친화적입니다.
헤더 vs meta 태그
가능하면 HTTP 응답 헤더 Content-Security-Policy 로 설정하세요. meta 태그는 일부 디렉티브(frame-ancestors, report-uri, sandbox 등)를 지원하지 않고, 페이지가 파싱되기 전 리소스에는 적용되지 않는 한계가 있습니다. 도입 단계에서는 Content-Security-Policy-Report-Only 로 며칠 운영하면서 console과 report endpoint 로그를 모니터링해 false positive 를 잡고, 안정화되면 enforcing 모드로 전환하는 것이 정석 절차입니다.
자주 묻는 질문 (FAQ)
Q. 'unsafe-inline'을 꼭 써야 하나요?
A. 가능하면 피하세요. nonce 또는 hash 기반 허용이 표준이며, Google CSP Evaluator 도 'unsafe-inline'을 강한 경고로 분류합니다.
Q. frame-ancestors와 X-Frame-Options 중 뭘 써야 하나요?
A. frame-ancestors가 우선이며 X-Frame-Options를 대체합니다. CSP를 설정한다면 X-Frame-Options 는 레거시 브라우저 호환용으로만 함께 두는 편이 좋습니다.
Q. report-uri는 어떻게 받나요?
A. CSP 위반이 발생하면 브라우저가 지정 URL로 JSON POST를 보냅니다. report.uri.com, sentry 등의 외부 서비스를 쓰거나 자체 엔드포인트로 수신할 수 있습니다. 최신 표준은 report-to + Report-To 헤더입니다.