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

Trusted Types 不能在底层“阻止注入”,它只在运行时拦截特定 DOM 操作的非法字符串赋值;真正起作用的前提是 CSP 头已启用 require-trusted-types-for 'script',否则策略函数只是普通 JS,完全不生效。
必须通过 HTTP 响应头开启强制模式,JS 创建策略本身不拦截
浏览器只在收到正确的 CSP 响应头后,才对危险操作做运行时检查。光写 trustedTypes.createPolicy() 不会拦任何东西。
-
Content-Security-Policy: require-trusted-types-for 'script'; trusted-types default;是最低要求——default策略名必须存在,否则TrustedHTML构造失败直接抛错 -
trusted-types后列出的策略名(如default、htmlPolicy)必须与 JS 中createPolicy('xxx')的名字严格一致,大小写敏感 -
meta标签无法设置require-trusted-types-for,该指令只接受 HTTP 响应头 - 开发阶段可用
Content-Security-Policy-Report-Only先收集违规,避免上线即崩
哪些操作会被拦截?哪些不会?
拦截范围由规范明确定义,不是所有字符串写入都管。关键看目标是否属于“执行上下文敏感”的 sink。
- 会被拦截:
element.innerHTML、element.outerHTML、element.insertAdjacentHTML()、document.write()、eval()、setTimeout('string')、location.href = string - 不会拦截:
element.textContent、element.setAttribute('data-value', string)、JSON.parse()、element.className——这些本身不触发脚本执行 - 注意:
element.insertAdjacentText()安全,无需 Trusted 类型;但element.shadowRoot.innerHTML在部分浏览器中未被拦截,需额外确认
策略函数里别做 HTML 过滤,优先用 textContent 替代
把策略当成“自动净化器”是常见误区。正则替换、白名单解析、手动拼接 HTML 都不可靠且低效。
- 策略函数必须返回纯字符串,不是
TrustedHTML实例——浏览器内部会自动包装 - 禁止在策略里调用
eval、new Function或拼接字符串后return input,这等于把信任交给不可控逻辑 - 正确做法:能用
textContent就不用innerHTML;必须插 HTML 时,先用DOMPurify.sanitize()在策略外处理,再传给policy.createHTML() - 示例错误写法:
createHTML: input => input.replace(/<script>/gi, '')</script>—— 绕过方式太多,且性能差
漏掉一个 sink 或间接调用,整个防护就失效
攻击者会主动寻找未受控入口。框架封装、Web Components、自定义属性赋值都可能逃逸。
-
el.outerHTML = string和el.innerHTML同样被拦截,但el.setAttribute('innerHTML', string)无效(不是标准 sink) - React/Vue/Angular 等框架内部的 DOM 操作是否走 Trusted Types,取决于其版本和配置——不能默认信任
- 动态生成 script 标签(如
document.createElement('script').src = string)也属于 sink,需用createScriptURL策略 - 最易忽略的是
location.assign(string)和iframe.srcdoc,它们同样需要对应策略类型
真正难的不是写策略,而是确保所有 sink 调用路径都被覆盖,且没有框架或库绕过类型检查——这点在大型项目中几乎必然出问题,必须配合 CSP report-only 模式持续观测。

















