Cross-site scripting (XSS) 攻击的核心在于攻击者能够向网页中注入恶意脚本,而浏览器却无法区分这些脚本与开发者编写的合法代码。传统的防御手段如输入过滤、输出编码虽然有效,但往往依赖于开发者的自觉性和一致性,容易出现疏漏,形成复杂应用中的安全短板。现在,一种名为 Trusted Types 的浏览器原生 API 正在从根本上改变这一攻防格局。它通过强制对危险的 Web API 调用进行类型化验证,将安全策略从“建议性”转变为“强制性”,从而在浏览器层面为动态内容构建了一道坚固的防线。

Trusted Types 的核心原理:从字符串到可信对象

传统上,浏览器接收字符串并将其直接赋值给如 innerHTMLscript.srceval() 等“注入点”(Sink)。Trusted Types 的原理是拦截这一过程。它要求开发者不是直接传递原始字符串,而是先创建一个经过策略验证的、类型化的“可信对象”(如 TrustedHTMLTrustedScriptURL),只有这些对象才能被赋值给危险的 DOM API。浏览器会强制执行这一规则,任何试图绕过它、直接传递字符串的行为都将被阻止并报错。这相当于在危险的 API 前设置了一个强制安检门,只有持有特定“安全通行证”的内容才能通过。

如何启用与实施 Trusted Types 策略

启用 Trusted Types 非常简单,只需在 HTTP 响应头中设置一个策略。这个策略定义了如何创建可信对象的规则。

Content-Security-Policy: trusted-types myPolicy;

这行代码告诉浏览器,页面只允许通过名为 “myPolicy” 的策略来创建可信类型。接下来,你需要在 JavaScript 中定义这个策略。策略本质上是一个工厂函数,用于处理输入的字符串并返回相应的可信对象。一个基础的策略定义如下:

if (window.trustedTypes && window.trustedTypes.createPolicy) {
  const escapeHTMLPolicy = trustedTypes.createPolicy('myPolicy', {
    createHTML: (input) => {
      // 在这里对输入进行净化和转义
      return input.replace(/&/g, '&')
                 .replace(//g, '>')
                 .replace(/"/g, '"')
                 .replace(/'/g, ''');
    },
    createScriptURL: (input) => {
      // 验证URL是否来自可信源
      if (input.startsWith('https://cdn.trusted.com/')) {
        return input;
      }
      throw new Error('Untrusted script URL.');
    }
  });
}

定义好策略后,所有对危险 API 的赋值都必须使用策略创建的可信对象:

// 错误:直接传递字符串,将被浏览器阻止
// document.getElementById('widget').innerHTML = userInput;

// 正确:通过策略创建 TrustedHTML 对象
const safeHTML = escapeHTMLPolicy.createHTML(userInput);
document.getElementById('widget').innerHTML = safeHTML;

这样,即使 userInput 中含有恶意脚本,也会在 createHTML 函数中被转义,最终插入 DOM 的是一段安全的文本,而非可执行的代码。

Trusted Types 的三种主要防御类型详解

Trusted Types 主要针对三类最常被利用的注入点,提供了对应的可信类型。

1. TrustedHTML: 用于防御 HTML 片段注入。这是防御反射型和存储型 XSS 的关键。它保护 innerHTMLouterHTMLdocument.write() 等 API。策略中的 createHTML 函数是实施过滤和净化的核心位置,可以集成成熟的库如 DOMPurify。

2. TrustedScriptURL: 用于防御脚本URL注入。它保护如 script.srciframe.srcembed.src 等需要加载外部资源的属性。策略中的 createScriptURL 函数应严格实施源(Origin)检查,确保只加载来自白名单域的脚本。

3. TrustedScript: 用于防御内联脚本或字符串动态执行。它保护 eval()setTimeout(string)new Function(string) 等 API。由于内联脚本本身风险极高,现代安全实践强烈建议避免使用,因此 TrustedScript 的使用场景相对较少,更多是作为一种兜底的强制约束。

与现有安全机制(CSP)的协同增强

Trusted Types 并非取代现有的内容安全策略(CSP),而是对其进行了革命性的增强。传统的 CSP 指令如 script-src 主要控制脚本的“来源”,但对于由应用自身动态生成的脚本内容(即“内联”逻辑)往往需要妥协性地启用 unsafe-inline,这带来了巨大风险。Trusted Types 完美地填补了这一空白。当与 CSP 结合使用时,你可以配置一个极其严格的策略:

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

这个配置意味着:脚本只能从同源加载(script-src 'self'),并且所有传递给脚本执行相关 API 的内容都必须经过 Trusted Types 处理(require-trusted-types-for 'script')。即使攻击者找到了一个存储型漏洞并注入了恶意字符串,由于该字符串无法通过可信类型策略的验证,攻击也会在最后一步被浏览器强制中止。

实际部署的挑战与最佳实践

尽管 Trusted Types 优势明显,但在大型遗留代码库中部署并非一蹴而就。主要挑战在于需要识别和修改所有使用危险 API 的代码点。为此,浏览器提供了报告机制,可以通过 Content-Security-Policy-Report-Only 头在监控模式下运行,收集违规报告而不实际阻断功能,帮助开发者逐步修复。

最佳实践路径分为四步:首先,启用报告模式,全面审计现有代码的违规情况。其次,为最核心、风险最高的模块(如用户评论、富文本编辑器)创建并应用可信类型策略。然后,利用自动化工具或代码库模式(如默认策略)逐步覆盖其他模块。最后,将报告模式切换为强制执行模式。对于第三方库,应选择本身就支持 Trusted Types 的库,或将其封装在自定义策略中。

技术前瞻与生态系统影响

Trusted Types 代表了前端安全范式从“检测与修复”到“预防与默认安全”的深刻转变。它正在被主流浏览器广泛支持,并逐渐成为现代 Web 框架和库的推荐安全实践。未来,我们可能会看到更多围绕 Trusted Types 的工具链出现,例如静态分析工具能自动识别代码中的注入点并建议策略,构建工具能自动集成净化库到策略创建过程中。此外,这一理念可能扩展到其他安全领域,例如为 Web API 调用、数据绑定等引入更多类型的“可信”验证。对于开发者而言,尽早采纳 Trusted Types 不仅是为应用筑牢防线,更是主动适应 Web 平台向更安全、更健壮方向发展的必然选择。它将安全责任从开发者个人的谨慎,部分转移到了可审计、可强制执行的平台机制上,这无疑是构建复杂、安全 Web 应用的基石技术。