使用 Trusted Types 防御 DOM 型 XSS

在 Web 安全领域,XSS(跨站脚本攻击)长期占据 OWASP Top 10 的显著位置。其中,DOM 型 XSS 因其不依赖服务器端渲染、完全在浏览器中触发而尤为隐蔽。传统的输入过滤和输出编码在面对复杂的前端框架和动态 DOM 操作时往往力不从心。Trusted Types 作为一项由 Google 主导推动的浏览器安全规范,为根治 DOM 型 XSS 提供了一种系统化的方案。

一、XSS 的常见方式与前端防御困境

XSS 通常分为三类:存储型、反射型和 DOM 型。存储型 XSS 将恶意脚本持久化保存在服务器数据库中;反射型 XSS 通过 URL 参数将恶意脚本“反射”回页面;而 DOM 型 XSS 则完全发生在客户端——攻击者通过控制 URL 片段、表单输入或其他客户端数据源,诱使前端 JavaScript 将不可信数据写入危险的内联执行点。

常见的 DOM 型 XSS 触发点包括:

  • element.innerHTML = userInput
  • document.write(userInput)
  • eval(userInput)
  • setTimeout(userInput, 0)
  • script.src = userInput
  • a.href = 'javascript:' + userInput

传统前端防御手段主要有三种:一是对用户输入进行转义或编码;二是使用安全的 API,如 textContent 代替 innerHTML;三是通过 CSP(内容安全策略)限制脚本执行。然而,这些方法高度依赖开发者的安全意识和代码审查,在大型项目中极易出现疏漏。一个被遗忘的 innerHTML 赋值就可能让整个防御体系功亏一篑。

二、Trusted Types 的核心思想

Trusted Types 的设计哲学是“默认拒绝,显式信任”。它通过浏览器层面的强制机制,禁止将普通字符串直接赋值给危险的内联执行点(称为“注入汇点”)。开发者必须使用 TrustedHTMLTrustedScriptTrustedScriptURL 等经过策略工厂创建的对象,才能写入这些位置。

启用 Trusted Types 后,以下代码将直接抛出 TypeError:

document.getElementById('output').innerHTML = '<b>Hello</b>'; // 被阻止

而正确的做法是:

const policy = trustedTypes.createPolicy('myPolicy', {
  createHTML: (input) => DOMPurify.sanitize(input)
});

document.getElementById('output').innerHTML = policy.createHTML('<b>Hello</b>');

通过这种方式,所有进入危险汇点的数据都必须经过策略函数的处理。策略函数中可以集成 DOMPurify 等成熟的净化库,也可以实现自定义的白名单逻辑。浏览器会强制检查每一个赋值操作,从根本上杜绝了意外引入的 XSS 漏洞。

三、在项目中落地 Trusted Types

1. 启用 CSP 指令

首先需要在 HTTP 响应头中启用 Trusted Types 策略:

Content-Security-Policy: require-trusted-types-for 'script'; trusted-types myPolicy

require-trusted-types-for 'script' 指示浏览器对所有脚本注入汇点强制类型检查。trusted-types 指令则指定允许创建哪些策略名称,防止攻击者通过注入脚本创建自己的策略。

2. 定义策略

在应用初始化阶段创建策略。策略名称应具有唯一性,避免与第三方库冲突:

if (window.trustedTypes && trustedTypes.createPolicy) {
  const sanitizePolicy = trustedTypes.createPolicy('default', {
    createHTML: (string) => DOMPurify.sanitize(string, { RETURN_TRUSTED_TYPE: true }),
    createScriptURL: (string) => {
      const url = new URL(string, document.baseURI);
      if (url.origin === location.origin) return url.href;
      throw new Error('Untrusted script URL: ' + string);
    }
  });
}

3. 处理第三方库兼容性

许多第三方库(如早期版本的 jQuery、Vue 2 的部分插件)仍在使用 innerHTML。启用 Trusted Types 后这些库可能报错。解决方案有两种:一是升级到支持 Trusted Types 的版本;二是为特定库创建宽容策略,但需谨慎评估安全风险。

4. 渐进式迁移

对于存量项目,建议先以报告模式运行:

Content-Security-Policy-Report-Only: require-trusted-types-for 'script'; report-uri /csp-report

收集违规报告,定位所有需要改造的代码点,逐步修复后再切换到强制模式。

四、与其他防御措施的协同

Trusted Types 并非孤立方案,它应与以下措施形成纵深防御:

  • CSP:Trusted Types 本身就是 CSP 的一部分,配合 script-src 限制外部脚本加载。
  • 输入验证:在数据进入系统时进行格式校验,减少不可信数据的来源。
  • 输出编码:在非 DOM 场景(如服务端模板渲染)中继续使用上下文相关的编码。
  • DOMPurify:作为策略函数中的净化引擎,处理富文本等复杂场景。

五、浏览器支持与未来展望

目前 Trusted Types 已在 Chrome、Edge 等 Chromium 内核浏览器中默认支持。Firefox 和 Safari 尚未完全实现,但可以通过 polyfill 在开发阶段模拟行为。随着 Web 安全形势的日益严峻,Trusted Types 有望成为未来 Web 标准的重要组成部分。

结语

DOM 型 XSS 的根源在于“字符串可以随意流入执行上下文”。Trusted Types 通过类型系统将这一隐式信任变为显式授权,让安全从“依赖开发者自觉”转变为“由浏览器强制执行”。虽然迁移过程需要一定成本,但对于安全要求较高的应用而言,这是一项值得投入的基础设施建设。结合 CSP、输入验证和净化库,Trusted Types 能够构建起一道真正可靠的 DOM 型 XSS 防线。

未经允许不得转载:任鹏个人博客 » 使用 Trusted Types 防御 DOM 型 XSS

赞 (0) 打赏

评论 0

取消
  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏