跳到主要内容

CSP与安全响应头

1. CSP 概念

Content Security Policy = 浏览器执行的脚本/样式白名单。即使有 XSS 漏洞,恶意脚本也跑不起来(不在白名单)。

2. 完整指令

Content-Security-Policy: default-src 'self';
script-src 'self' 'sha256-xxx' 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 wss://ws.example.com;
frame-src 'self';
frame-ancestors 'none';
object-src 'none';
base-uri 'self';
form-action 'self';
upgrade-insecure-requests;
report-uri https://example.com/csp-report
指令控制
default-src兜底(其他没设的用这个)
script-srcJS
style-srcCSS
img-src图片
font-src字体
connect-srcfetch / XHR / WebSocket
frame-srciframe 加载源
frame-ancestors谁能 iframe 我(取代 X-Frame-Options)
object-src<object> <embed>
base-uri<base> 标签
form-actionform action 目标
upgrade-insecure-requests自动 http→https

3. 源(source)值

含义
'self'当前 origin
'none'不允许任何
'unsafe-inline'内联 <script> <style>
'unsafe-eval'eval、Function、setTimeout(string)
'strict-dynamic'信任已经过 nonce 的脚本动态加载的脚本
'nonce-xxx'带特定 nonce 的内联
'sha256-xxx'哈希匹配的内联
https:任何 https
*.example.com通配子域
data:data URI
blob:blob URL

4. 内联脚本怎么办

4.1 nonce(推荐)

<script nonce="abc123">
console.log('inline')
</script>
Content-Security-Policy: script-src 'self' 'nonce-abc123'

每次请求 nonce 不同(服务端随机生成)。SSR 友好。

4.2 hash

<script>console.log('hi')</script>
Content-Security-Policy: script-src 'self' 'sha256-...'

计算 SHA256 加白名单。静态内容用。

4.3 unsafe-inline(慎用)

打开 = CSP 大半失效。

5. report-only 模式

不强制阻止,只上报,灰度上线 CSP:

Content-Security-Policy-Report-Only: default-src 'self'; report-uri /csp-report

收 1 周报告 → 调整规则 → 切到强制模式。

6. 部署步骤

  1. 用 report-only 收集真实违规
  2. 把合法第三方加白名单
  3. 切到 enforce
  4. 持续监控 violation report

7. 其他安全响应头

7.1 HSTS

Strict-Transport-Security: max-age=63072000; includeSubDomains; preload

强制 HTTPS。详见模块 02。

7.2 X-Content-Type-Options

X-Content-Type-Options: nosniff

禁止浏览器猜 MIME(防把 .txt 当 .html 解析)。

7.3 X-Frame-Options(被 CSP frame-ancestors 取代)

X-Frame-Options: SAMEORIGIN # 同源可 iframe
X-Frame-Options: DENY # 禁

新代码用 frame-ancestors

7.4 Referrer-Policy

Referrer-Policy: strict-origin-when-cross-origin

控制 Referer 头泄露。

含义
no-referrer不发 Referer
origin只发 origin(example.com)
strict-origin-when-cross-origin同源全发,跨源只 origin(推荐默认)
unsafe-url全发(不安全)

7.5 Permissions-Policy(前 Feature-Policy)

Permissions-Policy: camera=(), microphone=(), geolocation=(), interest-cohort=()

禁用浏览器特性。interest-cohort=() 关闭 Google FLoC(隐私)。

7.6 Cross-Origin-* 三件套

Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Embedder-Policy: require-corp
Cross-Origin-Resource-Policy: same-origin

启用 SharedArrayBuffer、防 Spectre 跨域。详见 web.dev/why-coop-coep

8. Nginx 配置

add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'nonce-${request_id}'; ..." always;
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;

9. 测试

securityheaders.com 评分。目标 A+。

CSP Evaluator 静态分析 CSP 配置。

10. 常见反模式

  • unsafe-inline + unsafe-eval 全开:CSP 等于没设
  • * 当白名单:失去意义
  • 直接上 enforce 没 report-only:业务功能被拦
  • nonce 写死字符串:失去防御意义(可预测)
  • 第三方统计要 unsafe-inline 就开了:建议换支持 nonce 的版本
  • HSTS preload 没准备直接申请:撤销极慢
  • iframe 同源不设 frame-ancestors 限制:被恶意嵌套

11. 延伸阅读