trusted types 通过 csp 头 require-trusted-types-for 'script' 拦截 eval 等字符串执行操作,强制用类型化策略替代动态脚本;需服务端配置 http 响应头、拆分最小权限策略、结构化替代字符串驱动逻辑。

Trusted Types 本身不直接“修复 eval”,而是让 eval 这类字符串执行型操作在启用策略后彻底失效——只要正确配置 CSP,任何 eval('...') 或 setTimeout('...', 100) 都会立即抛出 TypeError,根本无法执行。重构目标不是“安全地用 eval”,而是消灭所有字符串形式的动态执行入口,并用类型化、预定义、可审计的方式替代。
必须先激活浏览器级拦截机制
仅写 JS 策略毫无作用。关键动作是服务端返回以下 HTTP 响应头:
Content-Security-Policy: require-trusted-types-for 'script'; trusted-types default;-
trusted-types default表示只允许名为default的策略;若代码中调用trustedTypes.createPolicy('safeEval', {...})但未在 CSP 中声明,则创建失败 -
require-trusted-types-for 'script'是开关:它会让eval、setTimeout(字符串参数)、setInterval(字符串参数)、Function构造器(字符串参数)全部被浏览器拦截 -
不支持 meta 标签设置,必须走 HTTP 头;开发阶段可用
Content-Security-Policy-Report-Only先收集违规日志
识别并替换所有 eval 类危险调用点
Legacy 项目中容易被忽略的“伪 eval”场景远不止 eval() 函数本身:
-
setTimeout("doSomething()", 100)→ 改为setTimeout(doSomething, 100)(传函数引用) -
new Function("return " + userInput)→ 改为预编译逻辑,或使用JSON.parse()(仅限纯数据) -
location.href = "javascript:alert(1)"→ 改为location.assign(policy.createScriptURL("alert(1)"))(需 Policy 支持) - 第三方库中隐式调用(如某些老版模板引擎的
with+eval模式)→ 升级到支持 Trusted Types 的版本,或封装 wrapper 层拦截
按需创建最小权限 Script 策略
不要用一个策略包打天下。针对不同用途拆分,且禁止原始字符串透传:
- 仅允许执行已知白名单函数:
createScript: s => s.match(/^doLogin\(\)|doLogout\(\)|refreshToken\(\)$/) ? s : "" - 对接 JSON-RPC 调用:
createScript: s => s.startsWith("rpc.") && s.includes("(") && s.endsWith(")") ? s : "" - 完全禁用动态脚本:策略中直接
throw new Error("Dynamic script execution forbidden") - 注意:
createScript返回的是字符串,Trusted Types 内部自动包装为TrustedScript;返回空字符串或抛错即阻断
用结构化替代字符串驱动的异步流程
真正合规的重构,是把“运行时拼接指令”的模式,升级为“编译期确定行为”的模式:
- 将原
eval(userAction)替换为查表 dispatch:const handlers = { login: doLogin, logout: doLogout }; handlers[action]?.() - 后端返回结构化指令(如
{ type: "navigate", to: "/dashboard" }),前端用 switch 处理,而非拼接跳转语句 - 富文本中允许的交互(如点击高亮某段)→ 使用 data 属性标记意图:
<span data-action="highlight" data-id="123"></span>,统一由事件委托处理 - 所有用户输入最终只进入
textContent、dataset或JSON.parse(),彻底绕过执行上下文










