CSP(Content Security Policy, 콘텐츠 보안 정책)는 브라우저에게 허용된 리소스 출처와 실행 방식을 지시하는 HTTP 헤더다. XSS(크로스 사이트 스크립팅) 공격을 방어하는 가장 강력한 브라우저 수준 메커니즘이다.
기본 문법
Content-Security-Policy: 지시어1 값1 값2; 지시어2 값3;
예시:
Content-Security-Policy:
default-src 'self';
script-src 'self' https://cdn.example.com 'nonce-r4nd0m';
style-src 'self' 'unsafe-inline';
img-src 'self' data: https:;
connect-src 'self' https://api.example.com;
frame-ancestors 'none';
주요 지시어
| 지시어 | 제어 대상 |
|---|
| default-src | 미지정 리소스의 기본값 |
| script-src | JavaScript 실행 출처 |
| style-src | CSS 출처 |
| img-src | 이미지 출처 |
| connect-src | AJAX, WebSocket 연결 |
| frame-ancestors | 페이지를 iframe에 포함할 수 있는 출처 |
| upgrade-insecure-requests | HTTP 리소스를 HTTPS로 자동 업그레이드 |
소스 값
| 값 | 의미 |
|---|
| 'self' | 현재 출처만 허용 |
| 'none' | 모든 출처 차단 |
| 'unsafe-inline' | 인라인 스크립트/스타일 허용 (비권장) |
| 'unsafe-eval' | eval() 허용 (비권장) |
| 'nonce-xxx' | nonce 일치 시 허용 |
| 'strict-dynamic' | 신뢰된 스크립트가 로드한 스크립트 허용 |
| https: | 모든 HTTPS 출처 허용 |
Nonce 기반 CSP (권장)
python
import secrets
from flask import Flask, make_response, render_template_string
app = Flask(__name__)
@app.route('/')
def index():
nonce = secrets.token_urlsafe(16)
response = make_response(render_template_string("""
<script nonce="{{ nonce }}">
// 이 스크립트는 nonce 일치로 허용됨
console.log('안전한 스크립트');
</script>
""", nonce=nonce))
response.headers['Content-Security-Policy'] = (
f"default-src 'self'; script-src 'nonce-{nonce}' 'strict-dynamic'"
)
return response
CSP 보고 모드
# 차단하지 않고 위반 사항만 보고
Content-Security-Policy-Report-Only: default-src 'self'; report-uri /csp-report
# CSP 위반 보고 엔드포인트
POST /csp-report
{
"csp-report": {
"violated-directive": "script-src",
"blocked-uri": "https://evil.com/xss.js",
"document-uri": "https://example.com/page"
}
}
관련 개념