内容安全策略(CSP)
内容安全策略(Content Security Policy,简称 CSP)是一种强大的安全功能,可以帮助防止或减轻某些类型的安全威胁。它由网站向浏览器发送的一系列指令组成,指示浏览器对网站代码可以执行的操作进行限制。[1]
什么是 CSP
CSP 的主要用途是控制文档允许加载哪些资源,特别是 JavaScript 资源。这主要用作防御跨站脚本攻击(XSS)的手段,在这种攻击中,攻击者能够将恶意代码注入到受害者的网站中。[1]
CSP 还可以有其他用途,包括防御点击劫持和其他代码注入攻击。通过精确控制允许加载的内容,网站可以大大降低被攻击的风险。[2]
工作原理
实现方式
CSP 通过 HTTP 响应头 Content-Security-Policy 来实现。服务器在响应中包含此标头,浏览器会执行相应的限制策略。[3]
基本语法:
Content-Security-Policy: <policy-directive>; <policy-directive>例如:
Content-Security-Policy: default-src 'self'; script-src 'self' https://trusted.com除了 HTTP 响应头,也可以通过 HTML 的 <meta> 标签来设置 CSP:
注意: 使用<meta>标签时,某些 CSP 指令(如report-uri)将不起作用。推荐使用 HTTP 响应头方式。
向后兼容性
CSP 被设计为完全向后兼容。不支持 CSP 的浏览器会忽略它,按照默认的同源策略运行。如果网站不提供 CSP 标头,浏览器也会使用标准的同源策略。[2]
这意味着你可以安全地为网站添加 CSP,而不用担心破坏旧版浏览器的兼容性。
核心指令
default-src
default-src 指令是其他资源类型指令的后备方案。如果某个资源类型没有单独指定策略,将使用 default-src 的设置。[4]
Content-Security-Policy: default-src 'self'这表示默认情况下只允许加载同源资源。
script-src
script-src 指令控制 JavaScript 的有效来源,是防御 XSS 攻击最重要的指令之一。[5]
Content-Security-Policy: script-src 'self' https://trusted-cdn.com常用值:
'none'- 不允许加载任何脚本'self'- 只允许同源脚本'unsafe-inline'- 允许内联脚本(不推荐)'unsafe-eval'- 允许eval()等动态代码执行(不推荐)'nonce-<value>'- 允许带有特定 nonce 值的内联脚本'sha256-<hash>'- 允许匹配特定哈希值的内联脚本- 域名或 URL - 允许从指定来源加载脚本
style-src
控制样式表的来源:
Content-Security-Policy: style-src 'self' https://fonts.googleapis.comimg-src
控制图片的来源:
Content-Security-Policy: img-src 'self' data: https:connect-src
控制可以通过脚本接口加载的 URL(如 fetch、XMLHttpRequest、WebSocket):
Content-Security-Policy: connect-src 'self' https://api.example.comfont-src、media-src、object-src
分别控制字体、音视频、插件(如 <object>, <embed>)的来源。
frame-ancestors
控制哪些来源可以将当前页面嵌入到 <frame>, <iframe> 等元素中,用于防御点击劫持:
Content-Security-Policy: frame-ancestors 'none'sandbox
为请求的资源启用沙箱,类似于 <iframe sandbox> 属性的效果。[6]
实用技巧
使用 nonce 实现内联脚本安全
为了既使用内联脚本又保持安全,可以使用 nonce(随机数):
服务器响应头:
Content-Security-Policy: script-src 'nonce-2726c7f26c'HTML 中的脚本:
重要: nonce 值必须是随机生成的,每次请求都应该不同,且不可预测。
使用 hash 验证内联脚本
如果内联脚本是静态的,可以使用哈希值:
Content-Security-Policy: script-src 'sha256-xzi4zkCjuC8lZcD2UmnqDG0vFXPK+UZhV0kQOHpS0L0='这样只有内容匹配该 SHA-256 哈希值的脚本才能执行。
报告模式
在正式启用 CSP 之前,可以使用 Content-Security-Policy-Report-Only 头来测试策略,浏览器会报告违规但不会阻止资源加载:
Content-Security-Policy-Report-Only: default-src 'self'; report-uri /csp-violation-report配合 report-uri 或 report-to 指令,可以收集违规报告:
Content-Security-Policy: default-src 'self'; report-uri /csp-report当违反策略时,浏览器会向指定 URL 发送 JSON 格式的报告。
常见应用场景
基础防护配置
最小化的安全配置:
Content-Security-Policy: default-src 'self'这会阻止所有外部资源的加载,只允许同源资源。
允许特定 CDN
实际项目中通常需要加载第三方资源:
防御点击劫持
完全禁止页面被嵌入:
Content-Security-Policy: frame-ancestors 'none'只允许同源嵌入:
Content-Security-Policy: frame-ancestors 'self'完全禁用内联脚本和 eval
最严格的脚本策略:
Content-Security-Policy: script-src 'self'这会阻止所有内联脚本、eval() 和类似的动态代码执行。
最佳实践
1. 从严格策略开始
使用 default-src 'none',然后逐步添加必要的权限:
2. 避免使用 'unsafe-inline' 和 'unsafe-eval'
这两个关键字会大大降低 CSP 的安全性。如果必须使用内联脚本,优先考虑 nonce 或 hash 方式。
3. 使用报告模式测试
在生产环境应用 CSP 之前,先使用 Content-Security-Policy-Report-Only 进行充分测试,收集并分析违规报告。
4. 分环境配置
开发环境可能需要较宽松的策略(如允许 localhost),生产环境应使用严格策略:
// Node.js 示例
const csp = process.env.NODE_ENV === 'production'
? "default-src 'self'; script-src 'self'"
: "default-src 'self'; script-src 'self' 'unsafe-eval'";5. 配合其他安全头使用
CSP 应该与其他安全响应头配合使用:
Strict-Transport-Security: max-age=31536000; includeSubDomains
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
Content-Security-Policy: default-src 'self'浏览器兼容性
CSP 自 2016 年 8 月起在主流浏览器中得到广泛支持。但需要注意:[3]
- 某些高级特性(如 CSP Level 3 的新指令)可能在旧版浏览器中不支持
- 不同浏览器对某些指令的实现可能存在细微差异
- 建议在目标浏览器中进行充分测试
常见问题
CSP 阻止了我的内联脚本怎么办?
有三种解决方案:
- 将内联脚本移到外部文件(推荐)
- 使用 nonce: 为每个内联脚本添加唯一的 nonce 属性
- 使用 hash: 计算脚本内容的哈希值并添加到 CSP 策略中
如何调试 CSP 违规?
打开浏览器开发者工具的控制台,CSP 违规会显示为错误消息,包含详细的违规信息。Chrome 和 Firefox 都提供了良好的 CSP 调试支持。
CSP 会影响性能吗?
CSP 本身的性能开销非常小。浏览器在解析和执行 CSP 策略时的计算量很少。相反,通过阻止不必要的资源加载,CSP 可能会改善性能。
可以同时设置多个 CSP 头吗?
可以,但浏览器会取最严格的策略。如果设置了多个 Content-Security-Policy 头,所有策略都会生效,资源必须同时满足所有策略才能加载。
工具和资源
CSP 生成和验证工具
- Google CSP Evaluator: 分析和评估 CSP 策略的安全性
- CSP Builder: 可视化地构建 CSP 策略
- Report URI: CSP 违规报告收集服务
进一步学习
小结
CSP 是现代 Web 安全的重要组成部分,通过限制资源加载来源,可以有效防御 XSS、点击劫持等多种攻击。实施 CSP 的关键要点:
- 从严格策略开始,逐步放宽
- 避免使用
'unsafe-inline'和'unsafe-eval' - 使用 nonce 或 hash 处理必要的内联脚本
- 在生产环境部署前充分测试
- 配置违规报告以监控潜在问题
- 与其他安全措施配合使用
正确配置的 CSP 可以大大提升网站的安全性,是每个现代 Web 应用都应该考虑实施的安全机制。