Web Security

Web 安全:CSP 与 Trusted Types 深度实战

从 XSS 攻击原理出发,解析 Content Security Policy 的策略配置、Trusted Types API 的 DOM Clobbering 防御,以及在大型前端应用中的落地经验。

📅 2026-05-26 ⏱️ 约 20 分钟 👁️ 架构师视角

1. XSS 攻击机制与 CSP 的根本防御逻辑

跨站脚本(XSS)之所以长期危害严重,根源在于浏览器将用户输入当作可执行代码。传统防护依赖字符串过滤和正则匹配,但绕过技术层出不穷。 CSP(Content Security Policy)的思路完全不同:通过声明式策略,明确告诉浏览器哪些资源可以执行,其他一律拒绝。

CSP 的核心指令 default-srcscript-srcstyle-src 构成白名单机制。即使攻击者成功注入脚本,若该域名不在 script-src 允许列表中,浏览器不会执行。

注意:CSP 无法防御 DOM XSS——若应用通过 innerHTML 直接解析用户输入, CSP 不会拦截,因为此时请求早已到达浏览器,攻击代码在响应体内已不存在。Trusted Types 是为此场景设计的补充方案。

2. CSP 指令体系与 Report-Only 渐进增强

完整的 CSP 策略包含数十条指令。生产环境中推荐使用 Report-Only 模式先观察冲突,再逐步收紧策略。

# Report-Only 模式(仅报告,不拦截)
Content-Security-Policy-Report-Only:
  default-src 'self';
  script-src 'self' 'nonce-abc123';
  style-src 'self' 'sha256-xxxxx';
  report-uri https://csp-report.example.com/collect;

nonce 指令是现代 CSP 的推荐方式:服务端为每个页面请求生成随机 nonce,inline script 标签携带相同 nonce 值才能执行。这比 'unsafe-inline' 安全得多,比 SHA256 hash 更适合动态内容。

指令作用推荐值
default-src所有资源的默认源'self'
script-srcJS 执行源'self' 'nonce-xxx'
style-srcCSS 源'self' 'sha256-xxx'
img-src图片源'self' data: https:
connect-srcfetch/xhr/ws 目标'self' api.example.com
report-uri违规报告收集自建 CSP 报告服务

3. Trusted Types API:根治 DOM XSS 的最后防线

DOM XSS 是 CSP 无法覆盖的漏洞:攻击代码在 JavaScript 中生成(如 element.innerHTML = userInput),此时浏览器已收到同源响应, CSP 束手无策。

Trusted Types 通过强制类型化将所有 DOM 写入操作收口。启用后,以下模式会抛出 TypeError:

// 启用前(可被 DOM XSS 利用)
element.innerHTML = userInput;

// 启用后(需通过 Trusted Types 策略创建)
policy = trustedTypes.createPolicy('myPolicy', {
  createHTML: (input) => sanitize(input)
});
element.innerHTML = policy.createHTML(userInput);
关键价值:Trusted Types 将所有 innerHTMLevaldocument.write 调用强制收口到策略函数中,即使攻击者找到注入点,也无法直接写入恶意代码。Google 的内部数据表明,Trusted Types 将 DOM XSS 漏洞减少了 70%。

4. DOM Clobbering:被忽视的攻击向量

DOM Clobbering 通过在 HTML 中注入同名元素来覆盖全局变量。攻击者利用 <form id="fetch"><input name="src"> 可以劫持 window.fetch。这在 CSP script-src 'self' 场景下尤为危险——攻击者无需执行 JS 即可实现请求劫持。

Trusted Types 的另一价值在于消除 DOM Clobbering:当所有 DOM 写入都通过策略时,攻击者注入的 HTML 元素无法影响应用逻辑。

5. 落地实战:从 Report-Only 到 Enforce

推荐三阶段落地:

阶段 1(Week 1-2):全站部署 Content-Security-Policy-Report-Only: default-src 'none'; report-uri /csp-report,收集所有违规报告,识别第三方脚本和 inline script 清单。

阶段 2(Week 3-6):为所有 inline script 添加 nonce,为第三方域名添加白名单,处理 Report-Only 中的误报。

阶段 3(Week 7+):切换到 Enforce 模式,同时保留 Report-Only 用于持续监控。同时开启 Trusted Types,逐步将 innerHTML 调用迁移到策略中。

架构建议:大型应用通常有数十个第三方脚本(Analytics、CDN SDK),为每个域名逐一添加到 CSP 白名单工作量大且易遗漏。建议先通过 Report-Only 数据生成基础策略,再用 csp-evaluator 工具检测配置弱点。