0%

内容安全策略(CSP)深入

前言

在现代 Web 应用中,跨站脚本攻击(XSS)依旧是最常见且危害最大的漏洞之一。即使前端开发者已经做过各种输入过滤和转义工作,攻击者仍可能通过第三方库、广告脚本或 DOM 操作将恶意代码注入页面。内容安全策略(CSP)应运而生,它是一种强大的防御机制,用于指定允许加载的资源来源,从而有效降低 XSS 风险。

本篇文章将深入探讨 CSP 的各项配置、典型实践以及排查技巧。内容将覆盖:

  1. CSP 基础概念与语法
  2. 指令详解(script-src、style-src、connect-src 等)
  3. 报告功能和调试方法
  4. 常见坑与绕过方式
  5. 实战范例:从无到有逐步增强
  6. 与其他安全机制的配合使用

希望读者通过本文,既能在项目中正确配置 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-srcnonce / hash 的运用非常关键。

内联脚本限制

默认情况下,script-src 'self' 会阻止所有内联脚本(例如 <script>alert(1)</script>),这对许多旧版库或模板生成器来说是个大问题。可以通过三种方式允许特定内联脚本:

  1. unsafe-inline:完全允许,不安全
  2. 指定 nonce-<random>:给 <script> 添加随机值且在 HTTP 头中声明
  3. 使用 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-srcfont-src 等指令控制静态资源,而 connect-src 影响跨域请求。它可以防止敏感 API 被劫持:

1
Content-Security-Policy: connect-src 'self' https://api.example.com;

该配置禁止页面向非指定域提交 AJAX 请求,有助于限制数据泄露。

报告机制

CSP 可以在检测到违规时发送报告,以便分析攻击活动。可通过 report-urireport-to 指令设置:

1
Content-Security-Policy: default-src 'self'; report-uri /csp-report-endpoint

实际项目中可使用专门的收集服务(例如 Sentry、Google CSP Evaluator)来集中管理这些报告。

实战案例:逐步强化

  1. 初期部署: 使用 Report-Only 模式监测现有内容是否合规。
1
Content-Security-Policy-Report-Only: default-src 'self'; report-uri /csp-report
  1. 修复违规: 根据报告调整代码或导航到允许的来源。
  2. 全面启用: 一旦无明显违规,切换到正式 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 等资源注入风险。虽然配置和调试稍有挑战,但结合报告和自动化工具,团队可以快速迭代,最终建立起安全可靠的前端防线。

继续保持警惕,定期回顾策略并监控违规报告,是保证长久安全的关键。