安全:前端XSS防御的另一种思路,用Trusted Types约束DOM操作。
前端安全的老话题里,XSS 始终是绕不过去的坎。我们试过手动转义、框架自动转义、CSP 白名单、DOMPurify 净化,甚至把 `innerHTML` 列为团队禁忌。但现实很骨感:只要业务里还有一处拼接字符串、一次第三方 SDK 注入、一个老代码里的 `document.write`,攻击者就可能找到缺口。传统的防御思路大多是“教育开发者记得转义”,或者“在运行时检测异常”,前者依赖人,后者容易漏。
有没有一种办法,能让浏览器直接帮我们守住 DOM 操作的底线?Trusted Types 就是为此而生。它不教你如何转义,而是规定:凡是危险的 DOM 写入点,只接受“可信类型”对象,普通字符串一律拒绝。这相当于给 DOM 操作加了一层编译期式的类型约束,把 XSS 防御从“靠自觉”变成“靠机制”。
XSS 防御的困境:为什么总是防不住
XSS 的本质是攻击者让浏览器把不可信数据当成代码执行。危险点往往集中在几个 sink:`innerHTML`、`outerHTML`、`insertAdjacentHTML`、`document.write`、`eval`、`setTimeout(string)` 等。框架通常会在模板层做转义,但一旦你为了渲染富文本、Markdown 或第三方组件而使用 `innerHTML`,就等于主动打开了后门。更麻烦的是,代码库越大、历史包袱越重,越难保证每一处都安全。CSP 虽然能限制脚本来源,但对内联注入和 DOM 型 XSS 的约束有限,而且配置复杂,容易误伤。
Trusted Types 是什么:让 DOM 操作有“类型”
Trusted Types 是 CSP 的一个扩展指令,核心是 `require-trusted-types-for 'script'`。开启后,浏览器会强制所有危险的 DOM 写入点只接受 `TrustedHTML`、`TrustedScript`、`TrustedScriptURL` 等对象,而不是普通字符串。这些对象只能通过你定义的 policy 创建。换句话说,浏览器不再相信任何字符串,只相信你明确标记为“可信”的内容。这样一来,即使攻击者能注入字符串,也无法直接把它塞进 `innerHTML`,因为类型不匹配会直接抛出 `TypeError`。
怎么用:一个策略集中净化
实际使用时,你需要创建一个或多个 policy,并在里面集中处理净化逻辑。比如:
if (window.trustedTypes && trustedTypes.createPolicy) {
const policy = trustedTypes.createPolicy('default', {
createHTML: (input) => DOMPurify.sanitize(input),
createScriptURL: (input) => {
// 只允许同源脚本
const url = new URL(input, location.origin);
if (url.origin === location.origin) return url.href;
throw new Error('Untrusted script URL');
}
});
// 之后所有 innerHTML 赋值都必须经过 policy
el.innerHTML = policy.createHTML(userInput);
}
配合 CSP 响应头:
Content-Security-Policy: require-trusted-types-for 'script'; trusted-types default
这样,任何直接写 `el.innerHTML = userInput` 的代码都会失败。你可以在开发阶段用 `Content-Security-Policy-Report-Only` 先收集违规报告,再逐步修复。
与传统方案的区别:从“记得转义”到“强制可信”
传统方案是“黑名单”或“白名单”式净化,依赖开发者主动调用。Trusted Types 则是“类型系统”式约束,把安全责任交给浏览器执行。它的优势在于:第一,无法绕过,除非你显式创建 policy;第二,净化逻辑集中管理,避免散落各处;第三,可以和 DOMPurify 等库结合,形成“策略 + 净化”的双层保险。更重要的是,它让安全代码和不安全代码在类型层面就区分开,代码 review 时一眼就能看出哪些地方在写 DOM。
落地建议:report-only、默认策略与迁移
直接全量开启 Trusted Types 可能会让老项目瞬间崩溃。建议分三步走:第一步,用 `Content-Security-Policy-Report-Only` 加 `require-trusted-types-for 'script'`,观察控制台报告,找出所有违规点;第二步,为常见场景创建 policy,比如 `default` 策略处理富文本,`script` 策略处理动态脚本;第三步,逐步把违规点改为 `policy.createHTML(...)`,最后切换为强制模式。对于第三方库,可以借助 Trusted Types polyfill 做兼容,或者用 `trusted-types` 指令的 `allow-duplicates` 等选项灵活控制。
局限与组合拳
Trusted Types 并非银弹。它主要覆盖 DOM 型 XSS 的 sink,对服务端模板注入、URL 跳转等场景仍需其他手段。而且目前浏览器支持还不够统一,Chromium 系支持较好,Firefox 和 Safari 仍在跟进,生产环境需要评估兼容性。但它的思路值得借鉴:与其在运行时和攻击者玩猫鼠游戏,不如在 API 层面提高攻击成本。把 Trusted Types 和 CSP、输入验证、输出编码、DOMPurify 组合起来,才能构建更立体的前端安全防线。
说到底,安全不是一次性的配置,而是持续约束和演进。Trusted Types 给了我们一个强有力的工具,让 DOM 操作不再“裸奔”。如果你的项目正在为 XSS 头疼,不妨从 report-only 开始,试试这条新思路。
转载请注明出处,版权归原作者所有。
管理员
黑卡会员



