
CSP 生成器:告别手写 Content Security Policy 头的痛苦
📷 Pixabay / PexelsCSP 生成器:告别手写 Content Security Policy 头的痛苦
Content Security Policy 保护网站免受 XSS 攻击。学习指令配置、常见错误和如何在不破坏网站的情况下测试 CSP。
Content Security Policy(CSP)是防御 XSS 攻击最有效的工具之一,但同时也是配置最令人头痛的 HTTP 头之一。CSP 头的语法复杂,一个指令名拼错了不会报错,而一个遗漏的指令可能就是安全漏洞或者把整个网站搞崩的原因。
这篇文章会系统讲解 CSP 的工作原理、核心指令的用法、常见陷阱,以及如何用 CSP 生成器 准确高效地生成 CSP 配置。
为什么 CSP 重要
XSS(跨站脚本攻击)长期以来是 OWASP Top 10 最危险漏洞之一。攻击者通过在网页中注入恶意 JavaScript,可以:
- 窃取用户的 session token 和 Cookie
- 代替用户发起请求(CSRF)
- 在页面中插入假冒的登录表单
- 读取页面上的敏感信息
输入验证是第一道防线,但实际情况往往是复杂的:富文本编辑器、第三方内容、遗留代码……总有漏网之鱼。CSP 是第二道防线——即使 XSS 注入成功了,CSP 也能阻止恶意脚本的执行。
CSP 的基本结构
CSP 通过 HTTP 响应头发送:
Content-Security-Policy: 指令1 值1 值2; 指令2 值1; ...
举个例子:
Content-Security-Policy: default-src 'self'; script-src 'self' https://cdn.example.com; img-src 'self' data: https:;
每个指令用分号分隔,指令名后跟一个或多个允许的来源,用空格分开。
核心 CSP 指令详解
default-src:兜底指令
其他未明确指定的资源类型会回退到 default-src:
default-src 'self';
'self' 表示只允许同源(相同协议+域名+端口)的资源。
script-src:最重要的指令
控制 JavaScript 的加载来源,是 CSP 中最关键的指令:
script-src 'self' https://cdn.jsdelivr.net https://www.googletagmanager.com;
常用来源值:
'self'— 只允许同源脚本'none'— 禁止所有脚本'nonce-随机值'— 只允许带有匹配 nonce 属性的内联脚本'strict-dynamic'— 允许已信任的脚本动态加载其他脚本https://example.com— 允许指定来源
生产环境绝对避免:'unsafe-inline' 和 'unsafe-eval'。这两个值会大幅削弱 CSP 的防护效果。
style-src:控制样式来源
style-src 'self' https://fonts.googleapis.com;
img-src:控制图片来源
img-src 'self' data: https:;
data: 允许 base64 内联图片,https: 允许所有 HTTPS 来源的外部图片。
font-src:控制字体来源
font-src 'self' https://fonts.gstatic.com;
connect-src:控制 Ajax 和 WebSocket
控制 fetch()、XMLHttpRequest、WebSocket 等的目标地址:
connect-src 'self' https://api.example.com wss://realtime.example.com;
frame-src:控制 iframe
frame-src 'none'; /* 禁止所有 iframe */
frame-src https://www.youtube.com; /* 只允许 YouTube */
form-action:控制表单提交目标
form-action 'self';
base-uri:限制 base 标签
防止攻击者插入 <base> 标签来劫持相对 URL:
base-uri 'self';
upgrade-insecure-requests:强制 HTTPS
自动将页面上的 HTTP 请求升级为 HTTPS:
upgrade-insecure-requests;
常见的配置陷阱
陷阱一:用 unsafe-inline 图省事
为了让内联脚本正常运行而加上 'unsafe-inline' 是最常见的错误,这基本上让 script-src 的防护形同虚设。
正确做法:使用 nonce。服务器每次请求生成一个随机字符串,只有带有匹配 nonce 的 script 标签才被允许执行:
<!-- 服务器端渲染时注入 nonce -->
<script nonce="aB3kR9mX2qL7">
// 这个脚本会被允许执行
</script>
CSP 头:
script-src 'self' 'nonce-aB3kR9mX2qL7';
由于 nonce 每次请求都不同,攻击者无法预测,因此无法利用这个通道注入脚本。
陷阱二:忘记第三方脚本
Google Analytics、Stripe、Intercom、Hotjar 等第三方服务需要从各自的 CDN 加载脚本,必须加入 script-src 白名单:
script-src 'self'
https://www.googletagmanager.com
https://www.google-analytics.com
https://js.stripe.com
https://widget.intercom.io;
部署 CSP 前,建议在浏览器控制台检查是否有资源被意外阻止。
陷阱三:connect-src 和 img-src 遗漏
Google Analytics 不只需要 script-src,它还需要:
img-src允许跟踪像素connect-src允许数据上报请求
script-src 'self' https://www.googletagmanager.com;
img-src 'self' https://www.google-analytics.com data:;
connect-src 'self' https://www.google-analytics.com;
陷阱四:不测试直接上线
直接把 CSP 推到生产环境很危险——一个漏掉的来源可能导致 JavaScript 报错、样式丢失或 API 请求失败。
report-only 模式:安全测试的利器
在正式应用 CSP 之前,先用 Content-Security-Policy-Report-Only 头测试:
Content-Security-Policy-Report-Only: default-src 'self'; script-src 'self'; report-uri /csp-violations
这个头的作用:
- 违规不会阻止任何内容的加载(网站照常工作)
- 所有违规都会被上报到 report-uri 指定的地址
- 你可以在生产流量中收集数据,找出需要白名单的资源
违规报告是 JSON 格式:
{
"csp-report": {
"document-uri": "https://example.com/page",
"violated-directive": "script-src-elem",
"blocked-uri": "https://external.example.com/script.js",
"original-policy": "default-src 'self'; script-src 'self'"
}
}
根据报告逐步完善白名单,直到没有误报,再切换到正式的 Content-Security-Policy 头。
实际配置示例
静态展示网站
Content-Security-Policy:
default-src 'self';
img-src 'self' data: https:;
style-src 'self';
font-src 'self';
form-action 'self';
base-uri 'self';
frame-ancestors 'none';
使用 Google Analytics 的营销网站
Content-Security-Policy:
default-src 'self';
script-src 'self' https://www.googletagmanager.com https://www.google-analytics.com;
style-src 'self' https://fonts.googleapis.com;
font-src 'self' https://fonts.gstatic.com;
img-src 'self' https://www.google-analytics.com data:;
connect-src 'self' https://www.google-analytics.com;
form-action 'self';
base-uri 'self';
带 Stripe 支付的 SaaS 产品
Content-Security-Policy:
default-src 'self';
script-src 'self' https://js.stripe.com;
style-src 'self' https://fonts.googleapis.com;
font-src 'self' https://fonts.gstatic.com;
img-src 'self' data: https:;
connect-src 'self' https://api.stripe.com https://api.yourapp.com;
frame-src https://js.stripe.com;
form-action 'self';
base-uri 'self';
upgrade-insecure-requests;
使用 nonce 的 SPA 应用
Content-Security-Policy:
default-src 'self';
script-src 'self' 'nonce-{服务器生成的随机值}' 'strict-dynamic';
style-src 'self' 'nonce-{服务器生成的随机值}';
img-src 'self' data: blob:;
connect-src 'self' https://api.yourapp.com;
font-src 'self';
form-action 'self';
base-uri 'self';
upgrade-insecure-requests;
用 meta 标签设置 CSP
如果没有服务器端控制权(如纯静态托管),可以用 HTML meta 标签设置 CSP:
<head>
<!-- 尽量放在 head 最前面 -->
<meta http-equiv="Content-Security-Policy"
content="default-src 'self'; script-src 'self'; style-src 'self'">
</head>
注意 meta 标签的限制:
- 不支持
frame-ancestors指令 - 不支持
report-uri指令 - 比 HTTP 头稍晚生效(页面开始解析后才生效)
优先使用服务器端 HTTP 头,meta 标签作为备选。
推荐实施流程
- 先用 report-only 头 — 不阻止内容,只收集违规数据
- 收集几天的违规报告 — 覆盖真实用户的使用场景
- 分析报告,补充白名单 — 区分合法资源和真正的安全威胁
- 切换到正式 CSP 头 — 数据充分后再强制执行
- 持续监控 — 新功能上线后检查是否引入了新的违规
为什么用生成器
CSP 头的手写难点在于:
- 指令名称多(十几个),记不全
- 来源语法有细节(
'self'需要引号,https:不需要,data:不需要,'nonce-xxx'需要引号) - 漏掉某个指令不会有错误提示,只会在浏览器控制台看到被拦截的资源
- 多个第三方服务的白名单管理越来越复杂
CSP 生成器 的优势:
- 可视化界面选择指令和来源
- 自动处理语法细节(引号、分号等)
- 内置常见第三方服务(Google Analytics、Stripe、Cloudflare 等)的预设
- 一键生成完整的 HTTP 头字符串,直接粘贴到服务器配置
总结
CSP 是现代 Web 安全体系中不可缺少的一环,正确配置可以大幅降低 XSS 攻击的危害。
核心要点:
- 避免
'unsafe-inline'和'unsafe-eval',用 nonce 代替 - 先用 report-only 模式在生产环境测试
- 不要忘记第三方服务需要的所有指令(script-src、connect-src、img-src 等都可能需要配置)
- 用
base-uri 'self'和form-action 'self'防御其他类型的注入 - 持续监控 CSP 违规报告
打开 CSP 生成器,一步步配置你需要的指令,生成正确的 CSP 头字符串,然后复制粘贴到你的服务器配置中——这比手写快多了,也可靠多了。