Trusted Types 必须通过 HTTP 响应头启用,<meta http-equiv> 对 require-trusted-types-for 无效;浏览器仅识别响应头中的 Content-Security-Policy,漏配则所有 sink(如 innerHTML)静默放行,零拦截。

Trusted Types 本身不靠 HTML 标签启用,它必须通过 HTTP 响应头强制激活;写在 <meta> 里或只在 JS 里定义策略,完全不拦截 innerHTML 等操作。
为什么 <meta http-equiv="Content-Security-Policy"> 不起作用
浏览器明确不支持对 require-trusted-types-for 指令使用 <meta> 标签。该指令只认 HTTP 响应头中的 Content-Security-Policy。即使你写了:
<meta http-equiv="Content-Security-Policy" content="require-trusted-types-for 'script'; trusted-types default">
——所有 innerHTML 赋值仍会静默通过,零拦截、零报错。
- 服务端必须返回真实响应头,例如 Nginx 配置:
add_header Content-Security-Policy "require-trusted-types-for 'script'; trusted-types default"; - 开发阶段可用
Content-Security-Policy-Report-Only+report-uri先收集违规,避免上线后大面积白屏 - 策略名
default必须显式出现在trusted-types白名单中,否则trustedTypes.createPolicy('default', {...})创建失败时不会降级,直接抛TypeError
如何在 JS 中正确定义并使用 createHTML
策略函数不是过滤器,而是“封装器”:它接收原始字符串,返回一个字符串,由浏览器自动包装为 TrustedHTML 实例。不能手动 new TrustedHTML(),也不能返回 DOMPurify 的非 Trusted 类型结果。
立即学习“前端免费学习笔记(深入)”;
- ✅ 正确写法:
createHTML: input => DOMPurify.sanitize(input, { RETURN_TRUSTED_TYPE: true })(需 DOMPurify ≥ 3.0 且启用该选项) - ❌ 错误写法:
createHTML: input => new TrustedHTML(input)(浏览器不暴露构造函数,运行时报错) - ❌ 危险写法:
createHTML: input => input.replace(/&/g, '&')(纯文本转义无法防<img src=x onerror=alert(1)>类绕过) - 优先用
textContent替代innerHTML——90% 的场景根本不需要解析 HTML
哪些 DOM 操作会被拦截,哪些不会
拦截只发生在规范明确定义的“脚本执行类 sink”上,不是所有字符串赋值都管。关键看是否可能触发 JS 执行。
- ✅ 被拦截:
el.innerHTML = ...、el.insertAdjacentHTML(...)、document.write(...)、location.href = 'javascript:...'、eval(...)(字符串形式) - ✅ 同样受控但易漏:
el.outerHTML = ...、iframe.srcdoc = ...、DOMParser.parseFromString(..., 'text/html') - ❌ 不拦截:
el.textContent = ...、el.setAttribute('data-value', ...)、el.className = ...、JSON.parse(...) - 框架行为不确定:React 的
dangerouslySetInnerHTML是否被拦,取决于其内部是否调用标准 DOM API;Vue 3 默认不兼容,需升级到支持 Trusted Types 的版本
常见失效场景和绕过点
策略写得再严,只要漏掉一个环节,整个防护就归零。最常踩的坑不在代码逻辑,而在部署与集成细节。
- 服务端没发 CSP 头,或头被 CDN/反向代理覆盖/清空
- 策略名大小写不一致:CSP 写
trusted-types MyPolicy,JS 却调用trustedTypes.createPolicy('mypolicy', {...}) - 动态加载的 JS(如模块联邦、微前端子应用)未同步加载策略,导致其 innerHTML 调用直接报错或跳过
- 用
el.setAttribute('innerHTML', html)这种无效属性名——不触发任何拦截,但实际无效果;攻击者若真这么写,反而安全;可一旦写成el['innerHTML'] = html,就又回到被拦路径 - Web Components 中通过
shadowRoot.innerHTML赋值:部分旧版 Chrome/Safari 未覆盖该路径,需额外检测或降级处理
真正难的不是写策略,是确保每个字节的 HTML 输出路径都经过同一套类型检查链路——从服务端模板、构建时预编译、运行时策略,到框架适配和第三方库兜底。少一环,XSS 就可能从那个缝隙里钻出来。



















