前言
在现代 Web 应用中,跨站脚本攻击(XSS)依旧是最常见且危害最大的漏洞之一。即使前端开发者已经做过各种输入过滤和转义工作,攻击者仍可能通过第三方库、广告脚本或 DOM 操作将恶意代码注入页面。内容安全策略(CSP)应运而生,它是一种强大的防御机制,用于指定允许加载的资源来源,从而有效降低 XSS 风险。
本篇文章将深入探讨 CSP 的各项配置、典型实践以及排查技巧。内容将覆盖:
- CSP 基础概念与语法
- 指令详解(script-src、style-src、connect-src 等)
- 报告功能和调试方法
- 常见坑与绕过方式
- 实战范例:从无到有逐步增强
- 与其他安全机制的配合使用
希望读者通过本文,既能在项目中正确配置 CSP,又能在遇到阻断问题时进行有效排查。
什么是 CSP?
CSP 全称 Content Security Policy,是通过 HTTP 响应头(或 <meta> 标签)下发的一组规则。浏览器在加载页面时,会根据这些规则决定哪些资源可以被加载和执行,哪些需要被阻止。
浏览器解析 CSP 时,会检查页面中的:
- 脚本(包括内联、外链、动态创建的脚本)
- 样式表(内联、外链)
- 图像、字体、媒体资源
- AJAX 请求
- 其他如
frame,object,worker等
若某项资源不符合 CSP 指定的来源列表,则会被禁止加载或执行,并可选择性地生成违规报告。
CSP through HTTP header
一般通过服务器设置:
1 | Content-Security-Policy: default-src 'self'; script-src 'self' https://apis.example.com; object-src 'none'; report-uri /csp-report-endpoint |
或者使用强化的 Content-Security-Policy-Report-Only 头来先监测效果而不阻断。
CSP 语法与指令
CSP 规则由若干指令组成,每个指令控制一种资源类型,并附带允许的源列表。以下指令是常用的:
default-src:默认资源策略,若其他指令未指定,则使用此策略。script-src:脚本来源。style-src:样式来源。img-src:图像来源。connect-src:通过 XHR、Fetch 或 WebSocket 连接的目标。font-src:字体来源。object-src:<object>、<embed>、<applet>等元素的来源。media-src:音视频来源。frame-src/child-src:<frame>、<iframe>的来源。worker-src:Web Worker 和 Shared Worker 来源。manifest-src:Web App Manifest 的来源。base-uri:允许的<base>元素 URL。form-action:允许的表单提交目标。frame-ancestors:允许作为<iframe>嵌入的父页面。sandbox:启用沙箱特性。
源表达式
源列表中的项可以是:
'self':当前源'unsafe-inline':允许内联资源(不安全)'unsafe-eval':允许eval()等执行代码 (极不安全)'none':不允许任何来源- URL 协议 + 域名(如
https://cdn.example.com) - 通配符
*或子域通配*.example.com - 数据 URI
data:
例如:
1 | script-src 'self' 'unsafe-inline' https://cdn.example.com |
表示脚本可以来自当前域、允许内联脚本、以及 cdn.example.com。
脚本相关指令详解
脚本是 XSS 的主要攻击载体,因此 script-src 和 nonce / hash 的运用非常关键。
内联脚本限制
默认情况下,script-src 'self' 会阻止所有内联脚本(例如 <script>alert(1)</script>),这对许多旧版库或模板生成器来说是个大问题。可以通过三种方式允许特定内联脚本:
unsafe-inline:完全允许,不安全- 指定
nonce-<random>:给<script>添加随机值且在 HTTP 头中声明 - 使用
sha256-<hash>:根据脚本内容哈希
示例:
1 | Content-Security-Policy: script-src 'nonce-abc123' 'strict-dynamic' https:; |
在上述规则下,浏览器仅允许带有 nonce="abc123" 属性的脚本执行,同时允许经过 strict-dynamic 处理的后续加载脚本来自任意 HTTPS 源。这种组合方式既保证了安全性,又兼顾了灵活性。
使用哈希
另一种更细粒度的控制方式是使用哈希值:
1 | Content-Security-Policy: script-src 'sha256-AbCdEf123456...'; |
如果内联脚本内容的 SHA256 哈希与头部指定值匹配,则浏览器允许执行。哈希方法适用于不便修改脚本标签但内容固定的场景。
‘unsafe-eval’ 和动态代码
'unsafe-eval' 会允许 eval()、new Function() 等运行时编译方法,这对 XSS 风险极大,因此应尽量避免。可以通过工具(如 CSP evaluator)检查代码是否依赖它,并寻求替代方案。
样式相关安全
style-src 指令则控制 CSS 的来源。与脚本类似,它也存在内联样式的问题。
1 | Content-Security-Policy: style-src 'self' 'unsafe-inline'; |
默认情况下仅允许外部样式表,引入 'unsafe-inline' 会允许 <style> 标签和 style="" 属性,但应与严格的规则结合,或通过哈希/nonce 方式进行授权。
资源加载和网络请求
img-src、font-src 等指令控制静态资源,而 connect-src 影响跨域请求。它可以防止敏感 API 被劫持:
1 | Content-Security-Policy: connect-src 'self' https://api.example.com; |
该配置禁止页面向非指定域提交 AJAX 请求,有助于限制数据泄露。
报告机制
CSP 可以在检测到违规时发送报告,以便分析攻击活动。可通过 report-uri 或 report-to 指令设置:
1 | Content-Security-Policy: default-src 'self'; report-uri /csp-report-endpoint |
实际项目中可使用专门的收集服务(例如 Sentry、Google CSP Evaluator)来集中管理这些报告。
实战案例:逐步强化
- 初期部署: 使用
Report-Only模式监测现有内容是否合规。
1 | Content-Security-Policy-Report-Only: default-src 'self'; report-uri /csp-report |
- 修复违规: 根据报告调整代码或导航到允许的来源。
- 全面启用: 一旦无明显违规,切换到正式 CSP 头并移除
Report-Only。
常见坑与绕过
- 误用通配符(
*)会放宽策略,建议避免。 - 使用
data:URI 时需谨慎,它可能被用于注入。 frame-ancestors忽略时页面可被钓鱼站点嵌套。
攻击者可能通过 CSP 缺陷利用 CSP Header 注入、clickjacking、或者利用浏览器漏洞绕过策略。因此,CSP 应与其他安全措施(如输入验证、HTTP-only Cookie)共同部署。
与其他安全机制的协同
| 机制 | 功能 | 优势 |
|---|---|---|
| CSP | 限制资源来源 | 防范 XSS、减少数据泄露 |
| X-Content-Type-Options | 防止 MIME 屏蔽 | 避免脚本伪装 |
| X-Frame-Options / frame-ancestors | 防止点击劫持 | 提升安全性 |
| Referrer-Policy | 控制引用头 | 保护隐私 |
综合使用这些头部,形成多层防护。
工具与资源
总结
CSP 是强大的内容安全策略,通过合理规划和逐步实施,它可以显著降低 XSS 等资源注入风险。虽然配置和调试稍有挑战,但结合报告和自动化工具,团队可以快速迭代,最终建立起安全可靠的前端防线。
继续保持警惕,定期回顾策略并监控违规报告,是保证长久安全的关键。