Trusted Types 不是可选补丁,而是强制类型化 DOM sink 的浏览器级安全机制;必须从数据源头重构模板渲染、动态插入等环节,禁用未包装的 innerHTML、location 跳转和字符串事件处理器,并按场景最小化创建 Policy。

为什么 Trusted Types 不是“加个插件就能过审”的补丁
Trusted Types 不是防御 XSS 的可选开关,而是强制将所有可能触发 DOM sink 的 API(如 innerHTML、document.write、eval、setTimeout 字符串参数等)变成类型受限的通道。一旦启用,任何未通过 TrustedHTML、TrustedScript 等类型包装的字符串输入,都会直接抛出 TypeError —— 浏览器层面拦截,不给执行机会。
这意味着:你不能在已有代码里“打个补丁”再调用 innerHTML = unsafeString;必须从数据流源头开始重构,把模板渲染、动态插入、富文本解析等环节全部纳入类型化管道。
哪些 DOM 操作必须重写?重点盯住这三类 sink
企业级合规审查(如 SOC2、ISO 27001 或招聘平台风控白皮书要求)会静态扫描 + 动态 hook 所有高风险 sink 调用。以下三类必须重写,且不能靠“try/catch 绕过”:
-
innerHTML、outerHTML、insertAdjacentHTML:所有接受 HTML 字符串的写入点,必须改用element.setHTML(...)配合自定义Policy -
location.href、location.assign、location.replace:跳转目标必须经policy.createURL()包装,否则被拦截 -
addEventListener的第三个参数若为字符串(如"return false"),或onxxx属性赋值(如el.onclick = "doSomething()"),全部禁用;必须使用函数引用
Policy 创建不是“放行一切”,而是按场景最小化授权
别用 trustedTypes.createPolicy("default", { createHTML: s => s }) —— 这等于没开。企业安全审计工具会直接标记该 Policy 为“弱策略”。正确做法是按用途拆分 Policy,并限制输入源:
- 仅允许渲染后端返回的结构化 JSON 数据 → 用
createHTML内部做白名单标签过滤(只保留p、span、br) - 仅允许插入由 Markdown 解析器生成的 HTML → 在 Policy 中集成已验证的
marked实例,禁止原始字符串透传 - 完全禁止
script、iframe、onerror等危险标签和属性,Policy 返回空字符串或抛出错误
示例:
const markdownPolicy = trustedTypes.createPolicy("markdown", {
createHTML: (raw) => {
const html = marked.parse(raw);
return html.replace(/<(script|iframe|object)[^>]*>/gi, "");
}
});
与 nodeMap / 虚拟 DOM 架构的兼容要点
如果你已在用 nodeMap 扁平化结构或虚拟 DOM(如 React/Vue),Trusted Types 不会自动适配。关键冲突点在于:
- React 的
dangerouslySetInnerHTML默认绕过 Trusted Types,必须用createRoot+ 自定义 renderer 封装,或改用textContent+ 元素树重建 -
nodeMap序列化为 HTML 字符串时,若直接拼接用户输入字段(如node.textContent),必须先走 Policy 包装,不能在HtmlBuilder里硬编码innerHTML = htmlStr - 所有动态生成的
URL(如图片 src、a href)必须经policy.createURL(),不能依赖后端返回的原始链接字符串
真正合规的重构,是在 DOM 构建链最上游就切断非类型化输入——不是“渲染完再消毒”,而是“不许带毒原料进厂”。这点常被忽略,直到审计报告里出现“Policy bypass via innerHTML assignment in HtmlBuilder.build()”才返工。

















