不配置 csp 响应头,trusted types 完全无效;必须设置 content-security-policy: require-trusted-types-for 'script'; trusted-types default 才启用防护,且仅对 innerhtml 等高危 dom 操作生效,需按场景拆分策略而非滥用 default。

不配置 CSP 响应头,Trusted Types 就是纯摆设——浏览器根本不会拦截任何非法赋值,JS 里注册策略、封装 HTML 全部无效。
必须先加 HTTP 头:require-trusted-types-for 'script' 才生效
Trusted Types 的防护能力完全依赖浏览器收到的 Content-Security-Policy 响应头。没这行头,所有策略注册、TrustedHTML 创建、甚至 document.createElement 都不受约束。
- 生产环境必需头:
Content-Security-Policy: require-trusted-types-for 'script'; trusted-types default; -
<meta http-equiv="Content-Security-Policy">无法启用require-trusted-types-for,浏览器明确忽略 - 本地开发可临时用
localhost+http://启动服务并配好响应头,否则连调试都看不到拦截日志 - 检查是否生效:打开 DevTools → Application → Clear storage → 刷新,再看 Console 是否出现
Trusted Types policy creation failed或Failed to set innerHTML
哪些 DOM 操作必须改?不是“所有”而是“高危插入点”
不是每个 DOM 方法都要套 Trusted Types,只盯住那些**直接执行或解析字符串为可执行内容**的操作。以下调用一旦传入原始字符串,且未配 CSP 头,就会在现代浏览器(Chrome 83+、Edge 83+、Safari 16.4+)中被静默失败或抛错:
-
element.innerHTML = rawString→ 必须改为element.innerHTML = policy.createHTML(rawString) -
element.insertAdjacentHTML(position, rawString)→ 同样需 wrap 成TrustedHTML -
document.write()/document.writeln()→ 已被主流策略默认禁止,必须重写为innerHTML+ 策略封装 -
location.assign(urlString)→ 若 url 是拼接生成,需用policy.createScriptURL(urlString),否则跳转会失败 -
elem.setAttribute('onclick', handlerCode)属于内联事件,已被require-trusted-types-for 'script'覆盖,无需额外处理
别写“万能 default”策略,按场景拆分策略名
用一个 default 策略兜底所有 HTML 拼接,等于把安全门锁换成贴纸——它通过了 CSP 校验,但完全失去语义隔离能力。真实项目中应按数据来源和渲染意图划分策略:
- 模板引擎输出(如 Handlebars 渲染结果)→ 单独建
handlebars-html策略,只允许已知安全的占位符替换,禁止接受用户输入直传 - 富文本编辑器内容 → 建
sanitized-html策略,内部集成 DOMPurify 或自定义白名单过滤,再 wrap 成TrustedHTML - 第三方库返回的 HTML(如 jQuery
.html())→ 查文档确认是否支持 Trusted Types;若不支持,用policy.createHTML($el[0].innerHTML)包一层代理 - 动态生成的 SVG 字符串 → 单独策略
svg-html,避免与普通 HTML 混用导致 XSS 绕过
legacy 项目改造最容易漏掉的三个点
重构时最常卡在“看起来跑通了,但审计仍不通过”,问题往往出在非主流程路径:
-
document.write可能藏在老旧 polyfill 或 analytics SDK 初始化脚本里,需全局 grepdocument\.write并替换为document.body.appendChild+createHTML - Webpack/Vite 构建产物中,
eval或new Function动态执行字符串,也会被require-trusted-types-for 'script'拦截,需改用import()或预编译函数 - SSR 渲染后客户端 hydrate 阶段,若用
innerHTML同步服务端 HTML,必须确保服务端也走同一策略生成,否则客户端校验失败触发 hydration mismatch
真正难的不是写策略,而是让每个 HTML 字符串都有明确的“出生证”:谁生成、为什么生成、是否含用户输入。没有这个上下文,策略就只是形式合规的空壳。










