CSP는 XSS 공격에 대한 가장 효과적인 방어 수단 중 하나입니다. 브라우저에 어떤 리소스를 로드할 수 있는지 알려줌으로써 취약점이 있어도 악성 코드 주입을 방지합니다.
CSP란? 왜 중요한가?
CSP는 브라우저가 로드할 수 있는 리소스를 제어하는 HTTP 응답 헤더입니다.
CSP 작동 원리
브라우저가 CSP 헤더를 받으면 규칙을 위반하는 리소스를 차단합니다:
Browser receives HTTP response:
Content-Security-Policy: default-src 'self'; script-src 'self' https://cdn.example.com
Browser allows:
✅ <script src="/app.js"> (same origin = 'self')
✅ <script src="https://cdn.example.com/lib.js">
Browser blocks:
❌ <script src="https://evil.com/steal.js">
❌ <script>alert('XSS')</script> (inline = not allowed)CSP 디렉티브 전체
| 디렉티브 | 제어 대상 | 예시 |
|---|---|---|
| default-src | Fallback for all resource types | 'self' |
| script-src | JavaScript files and inline scripts | 'self' https://cdn.example.com |
| style-src | CSS stylesheets and inline styles | 'self' 'unsafe-inline' |
| img-src | Images (img, favicon, CSS background) | 'self' data: https: |
| font-src | Web fonts (@font-face) | 'self' https://fonts.gstatic.com |
| connect-src | XHR, fetch, WebSocket, EventSource | 'self' https://api.example.com |
| media-src | Audio and video elements | 'self' |
| frame-src | Iframes and embedded frames | 'self' https://www.youtube.com |
| object-src | Plugins (Flash, Java applets) | 'none' |
| base-uri | Base element URLs | 'self' |
| form-action | Form submission targets | 'self' |
| frame-ancestors | Who can embed this page (anti-clickjacking) | 'self' |
| upgrade-insecure-requests | Upgrade HTTP requests to HTTPS | (no value needed) |
소스 값 설명
| 소스 | 의미 |
|---|---|
| 'self' | Same origin (protocol + host + port) |
| 'none' | Block all resources of this type |
| 'unsafe-inline' | Allow inline scripts/styles (reduces security!) |
| 'unsafe-eval' | Allow eval(), Function(), setTimeout(string) |
| 'strict-dynamic' | Trust scripts loaded by already-trusted scripts |
| 'nonce-abc123' | Allow specific inline script with matching nonce attribute |
| 'sha256-...' | Allow inline script matching this hash |
| https: | Allow any HTTPS resource |
| data: | Allow data: URIs (e.g., inline images) |
| blob: | Allow blob: URIs |
| *.example.com | Allow all subdomains of example.com |
단계별 구현
1단계: 리포트 전용 모드로 시작
사이트를 깨뜨리지 않고 위반 사항을 식별:
Content-Security-Policy-Report-Only: default-src 'self'; report-uri /csp-report3단계: 정책 구축
분석 결과를 바탕으로 정책 생성:
Content-Security-Policy:
default-src 'self';
script-src 'self' https://cdn.example.com;
style-src 'self' 'unsafe-inline';
img-src 'self' data: https:;
font-src 'self' https://fonts.gstatic.com;
connect-src 'self' https://api.example.com;
frame-ancestors 'self';
base-uri 'self';
form-action 'self';
object-src 'none';
upgrade-insecure-requests일반적인 CSP 설정
엄격한 정책
Content-Security-Policy:
default-src 'none';
script-src 'self';
style-src 'self';
img-src 'self';
font-src 'self';
connect-src 'self';
base-uri 'self';
form-action 'self';
frame-ancestors 'none';
object-src 'none';
upgrade-insecure-requests중간 (일반 SPA)
Content-Security-Policy:
default-src 'self';
script-src 'self' 'unsafe-inline' 'unsafe-eval';
style-src 'self' 'unsafe-inline';
img-src 'self' data: blob: https:;
font-src 'self' https://fonts.gstatic.com;
connect-src 'self' https://api.example.com wss://ws.example.com;
frame-ancestors 'self'Google Analytics 포함
Content-Security-Policy:
default-src 'self';
script-src 'self' https://www.googletagmanager.com https://www.google-analytics.com;
img-src 'self' https://www.google-analytics.com https://www.googletagmanager.com;
connect-src 'self' https://www.google-analytics.com https://analytics.google.com구현 방법
Nginx
add_header Content-Security-Policy "default-src 'self'; script-src 'self'" always;Apache
Header set Content-Security-Policy "default-src 'self'; script-src 'self'"Express.js
// Using helmet middleware (recommended)
const helmet = require('helmet');
app.use(helmet.contentSecurityPolicy({
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'", "https://cdn.example.com"],
styleSrc: ["'self'", "'unsafe-inline'"],
imgSrc: ["'self'", "data:", "https:"],
}
}));
// Or manually
app.use((req, res, next) => {
res.setHeader('Content-Security-Policy',
"default-src 'self'; script-src 'self'");
next();
});HTML Meta 태그
<meta http-equiv="Content-Security-Policy"
content="default-src 'self'; script-src 'self'">일반적인 실수
| 실수 | 영향 | 해결 |
|---|---|---|
| 스크립트에 unsafe-inline 사용 | XSS 보호 무효화 | nonce 또는 hash 사용 |
| unsafe-eval 사용 | eval() 허용 | eval() 없이 코드 리팩토링 |
| script-src에 와일드카드(*) | 모든 도메인 스크립트 허용 | 특정 도메인만 화이트리스트 |
| 인라인 스타일 미고려 | CSS-in-JS 차단 | style-src에 unsafe-inline 또는 nonce |
| 리포트 전용 테스트 생략 | 사이트 기능 중단 | 항상 리포트 전용부터 시작 |
| base-uri 미설정 | base 태그 주입 허용 | base-uri 'self' 추가 |
FAQ
CSP는 어떤 공격을 방지하나요?
주로 XSS 공격을 방지합니다. 클릭재킹, 데이터 주입, 혼합 콘텐츠 문제도 방지합니다.
CSP가 입력 검증을 대체하나요?
아니요. CSP는 심층 방어 수단입니다. 모든 사용자 입력의 검증과 새니타이즈는 여전히 필요합니다.
인라인 스크립트에서 CSP를 사용하려면?
nonce(랜덤 토큰) 또는 hash(SHA-256)를 사용합니다. unsafe-inline은 피하세요.
CSP가 웹사이트를 느리게 하나요?
아니요. CSP는 브라우저가 실행하며 성능 영향은 무시할 수 있습니다.