能彻底阻断 dom xss,但仅当启用 csp 头 require-trusted-types-for 'script' 并确保所有危险 dom 操作均通过 trusted types 策略接口执行;否则防护失效。

不能通过 CSP 配合 Trusted Types 实现“静态检查”。Trusted Types 是运行时强制机制,不是编译期或构建期的静态分析工具。它不扫描代码、不报 lint 警告、不生成报告——只在浏览器执行到危险 DOM 操作时,实时拦截非法赋值。
必须靠 HTTP 响应头触发运行时防护
Trusted Types 的拦截能力完全依赖服务端返回的 CSP 响应头。没有它,所有策略代码(哪怕写得再规范)都不会起作用。
- 必需指令:Content-Security-Policy: require-trusted-types-for 'script'; trusted-types default
- default 是硬性要求:若策略名不匹配,trustedTypes.createPolicy("default", {}) 会直接抛错,不会静默失败
- meta 标签无效:CSP 的 require-trusted-types-for 指令不支持 设置,必须走 HTTP 头
- 开发阶段可用 Report-Only:用 Content-Security-Policy-Report-Only 先收集违规点,避免上线后大面积报错
哪些 DOM 操作会被实时拦截
启用后,浏览器会在 JS 执行过程中对明确列入规范的“sink”进行类型校验。传入非 Trusted 类型即抛 TypeError。
- 被拦截的高危操作:innerHTML、outerHTML、insertAdjacentHTML、document.write、document.writeln、DOMParser.parseFromString(..., "text/html")、
- 不拦截的安全操作:textContent、setAttribute('data-*', ...)、className、innerText —— 这些本身不执行脚本,无需 Trusted 类型
- 容易被忽略的逃逸点:shadowRoot.innerHTML(旧版 Safari/Chrome)、el.setAttribute('innerHTML', ...)(属性名错误,不触发 sink)、第三方库未适配的内部调用
策略设计要避免“伪安全”陷阱
策略函数不是 HTML 过滤器,不能在里面写正则替换或手动转义。那既不可靠,也违背设计初衷。
- 正确做法:用 DOMPurify 提前净化富文本,再用策略包装成 TrustedHTML;模板渲染结果应来自已知安全上下文,禁止拼接用户输入
- 错误做法:在策略里写 html.replace(/<script>/gi, '') —— 绕过方式极多,且性能差</script>
- 权限最小化:不要用一个 "default" 策略处理所有场景。编辑器输出、模板渲染、第三方 SDK 注入应分设策略,互不越权
落地时的关键验证动作
上线前必须确认三件事是否全部满足,缺一不可:
- 服务端已下发正确 CSP 头:用浏览器 DevTools → Network → Headers 查看响应头是否含 require-trusted-types-for 和 trusted-types
- 所有 sink 调用都改用 Trusted 类型:搜索代码中 innerHTML =、insertAdjacentHTML(、document.write( 等,确保右侧是策略函数返回值,不是字符串
- 第三方库已兼容或被封装:jQuery 从 3.5+ 自动识别 TrustedHTML;未适配的库需加一层代理 wrapper,把字符串输入转为可信实例










