trusted types 不能杜绝 dom 型 xss,但能大幅压缩攻击面;需正确配置策略、覆盖所有 dom sink 且不绕过类型检查,否则防护失效。

Trusted Types 不能“杜绝” DOM 型 XSS,但它能大幅压缩攻击面——前提是策略配置正确、所有 DOM sink 被覆盖、且不绕过类型检查。
为什么 innerHTML 赋值会触发 Trusted Types 违规
浏览器在启用 Trusted Types 后,会对已知的危险 DOM sink(如 innerHTML、document.write、eval 等)做运行时拦截。只要传入的不是 TrustedHTML 实例,就会抛出 TypeError: Failed to set the 'innerHTML' property on 'Element': This document requires 'TrustedHTML' assignment.。
- 该拦截发生在 JS 执行阶段,不是编译期或 lint 阶段,所以必须开启策略并加载脚本后才生效
- 只有显式调用 sink 才被拦截;间接调用(如通过
el.outerHTML = ...或框架内部的 DOM 操作)可能逃逸,需确认框架是否兼容 Trusted Types -
innerHTML不是唯一 sink:还要关注insertAdjacentHTML、document.write、location.href(部分模式)、eval、setTimeout(字符串形式)等
如何定义一个最小可用的 Trusted Types 策略
策略本质是一个函数,接收原始字符串并返回对应类型的封装对象。最简策略只处理 innerHTML 场景,但生产环境应覆盖全部 sink。
if (window.trustedTypes && trustedTypes.createPolicy) {
trustedTypes.createPolicy('default', {
createHTML: string => {
// 示例:仅允许纯文本转义,禁止任何标签
return string
.replace(/&/g, '&')
.replace(/, '/g, '>')
.replace(/"/g, '"')
.replace(/'/g, ''');
}
});
}
- 策略名
'default'是关键:它会被浏览器默认策略回退机制使用;若不设,默认策略为拒绝所有 -
createHTML返回的是字符串,不是TrustedHTML对象——Trusted Types 内部会自动包装,开发者无需手动调用trustedTypes.emptyHTML等 - 不要在策略里做复杂 HTML 解析或白名单过滤:性能差、易绕过;应优先用
textContent替代,或用模板引擎预编译
常见绕过方式与防御要点
Trusted Types 容易被“策略外挂”或“sink 逃逸”绕过,以下操作会直接失效保护:
- 在策略中调用
eval、new Function或拼接字符串后return string—— 这等于把信任委托给不可控逻辑 - 使用非标准 sink,如
el.setAttribute('innerHTML', ...)(无效)或通过 Web Components 的shadowRoot.innerHTML(部分浏览器未拦截) - 加载策略前执行了危险操作:策略必须在任何 sink 调用前注册,推荐放在
内首个<script></script>,且用defer或同步加载 - 服务端未发送
Content-Security-Policy: require-trusted-types-for 'script';—— 浏览器不会强制启用,仅靠 JS 注册策略无实际拦截效果
真正难的是策略粒度控制:不同组件需要不同信任级别(比如富文本编辑器 vs 用户昵称),这时得创建多个命名策略(如 'rich-text'、'user-display'),并在调用处显式指定,而不是全靠 default 一把抓。漏掉一个 sink、写错一个策略名、或服务端 CSP 缺失,整套机制就形同虚设。










