trusted types 通过强制 dom 操作使用 trustedhtml 等类型实现安全升级,需配合 csp 响应头 require-trusted-types-for 'script' 和 trusted-types default 才生效,策略须提前注册、按场景拆分、仅封装已净化内容,禁用字符串直赋。

Trusted Types 本身不能“彻底重构”逻辑,但它能强制你把 DOM 操作从字符串驱动转向类型驱动——这才是通过现代安全审计的关键转变。核心不是加个策略函数,而是让所有高危写入点(innerHTML、insertAdjacentHTML、document.write 等)只接受 TrustedHTML 或 TrustedScriptURL 实例,且该实例必须由显式声明的策略生成。
必须先配 CSP 响应头,否则零防护
浏览器只在收到 HTTP 头后才拦截非法赋值。JS 里调用 trustedTypes.createPolicy 本身不产生任何防护效果。
- 生产环境必需头:
Content-Security-Policy: require-trusted-types-for 'script'; trusted-types default; -
default是强制要求的策略名——若策略名不匹配,createPolicy("default", {})会直接抛错,而非静默降级 -
require-trusted-types-for 'script'是开关:开启后,elem.innerHTML = "<img src="x" onerror="alert(1)">"立即报TypeError - 不支持
<meta http-equiv="Content-Security-Policy">设置该指令,必须走 HTTP 响应头 - 开发阶段可用
Content-Security-Policy-Report-Only先捕获违规,避免上线即崩
识别并替换全部高危 DOM sink,不止 innerHTML
legacy 项目中常被忽略的受控入口远不止 innerHTML。只要可能执行脚本或注入 HTML,就属于 sink,必须统一接管。
-
outerHTML、insertAdjacentHTML、document.write、document.writeln -
DOMParser.parseFromString(html, "text/html")返回的 Document 中若含脚本,也受控 -
<iframe srcdoc=""></iframe>和<object data=""></object>的属性值同样需TrustedHTML -
location.assign(url)若传字符串会被拦,必须传TrustedURL实例 -
eval()、setTimeout(string)、setInterval(string)同样被require-trusted-types-for 'script'覆盖
为不同场景设计最小权限策略,拒绝万能策略
一个“通用 HTML 清洗器”策略违背 Trusted Types 设计初衷,也容易因业务变化引入漏洞。应按数据来源和用途拆分策略。
- 纯内容渲染(如评论、文章正文):策略只允许白名单标签(
p、strong、ul等),剥离<script></script>/<style></style>和事件属性 - 富文本编辑器输出:可集成
DOMPurify.sanitize(),但净化后必须显式包装成TrustedHTML,不能返回原始字符串 - 模板拼接(如 Handlebars 渲染结果):策略应仅封装已知安全的模板输出,禁止接受用户输入直传
- 第三方库(如 jQuery):利用其内置适配能力,
$().html(getHtmlWrapper())可自动识别TrustedHTML;确认版本是否兼容,避免逃逸
策略实现要简洁可靠,不替代转义逻辑
策略函数不是 HTML 过滤器。在策略里写正则清洗、手动解析或白名单过滤,既易绕过又拖慢性能。
- 优先用
textContent替代innerHTML—— 绝大多数展示场景根本不需要 HTML - 必须插 HTML 时,应在策略外完成净化(如用 DOMPurify),再由策略做类型封装
- 策略函数必须返回
TrustedHTML对象,不能返回字符串;返回字符串会导致赋值失败或策略被跳过 - 策略对象字面量不能引用外部变量或闭包;策略名需与 CSP 中声明的完全一致
- 策略必须在任何 sink 调用前注册,推荐放在
内首个<script></script>,用defer或同步加载










