Trusted Types 不能修复 SQL 注入,它仅防御前端 XSS,作用于 innerHTML、eval 等 DOM 操作;SQL 注入需后端参数化查询和白名单校验,二者防护面完全不同。

Trusted Types 不能修复 SQL 注入,别混淆概念
Trusted Types 是浏览器端防御 XSS 的机制,和 SQL注入 完全无关。它管的是 DOM 操作(比如 innerHTML、eval()、document.write()),不是数据库查询。如果你在查“字符串连接导致的 SQL 注入”,启用 Trusted Types 不会拦截 fetch('/api?user=' + userInput),更不会阻止后端拼接 "WHERE name = '" + req.query.name + "'"。这种混淆常见于刚接触前端安全的同学——先确认攻击面:是服务端 SQL 构造?还是客户端 DOM 渲染?两者防护路径完全不同。
真正要重构的,是后端 SQL 字符串拼接逻辑
遗留项目里高频出现的危险模式包括:String sql = "SELECT * FROM users WHERE id = " + id、knex.raw("ORDER BY " + sortBy)、MyBatis ${tableName}。这些必须逐个定位并替换,不能靠前端机制兜底。实操建议如下:
- 全局搜索正则:
"\+\s*["'\`]?\w+["'\`]?\s*\+|`[^`]*\$\{[^}]+\}[^`]*`|\.raw\(|\.whereRaw\(|\$\{[a-zA-Z0-9_]+\} - 对每个匹配结果判断:该变量是否来自用户输入(
req.query、req.body、req.headers)、是否参与 SQL 结构(表名、列名、ORDER BY、IN子句) - 结构类动态项(如表名)必须走白名单校验:
if (!ALLOWED_TABLES.contains(tableName)) throw new IllegalArgumentException(); - 值类动态项(如
WHERE status = ?)必须改用参数化:PreparedStatement的setString(1, userInput),或 Knex 的.where('status', userInput)
为什么 ORM 的 .raw() 和 MyBatis ${} 是重灾区
很多团队误以为用了 ORM 就安全了,但 .raw()(Knex/Sequelize)、${}(MyBatis)、@Query(nativeQuery = true)(JPA)本质上就是开后门让字符串拼接重新入场。它们绕过了框架内置的参数化机制,直接把用户输入喂给数据库驱动。典型陷阱:
-
knex('users').whereRaw('name LIKE "%' + userInput + '%"'):通配符 + 拼接 = 双重风险 -
SELECT * FROM ${tableName}:哪怕tableName经过parseInt()或toLowerCase(),只要没进白名单,就可能被构造为users; DROP TABLE admins-- -
@Query(value = "SELECT * FROM users WHERE created_at > " + dateFilter, nativeQuery = true):Java 字符串拼接发生在编译期,Trusted Types 根本看不见
Trusted Types 能起作用的真实场景
如果你的遗留项目真有 DOM 级字符串拼接(比如用 innerHTML 渲染服务端返回的富文本、或动态生成 script 标签),那 Trusted Types 才是解药。配置示例如下:
const policy = trustedTypes.createPolicy('myPolicy', {
createHTML: (input) => DOMPurify.sanitize(input),
createScript: (input) => input.replace(/eval|function\(/g, '')
});然后强制所有危险 API 经由策略中转:el.innerHTML = policy.createHTML(userControlledHtml)。但注意:这无法防止 fetch() 发出恶意请求,也不能约束后端行为。真正的纵深防御,是前端用 Trusted Types 拦住 XSS,后端用参数化 + 白名单堵死 SQL 注入,二者缺一不可。
最容易被忽略的一点:Trusted Types 的 createScriptURL 策略对 <script src></script> 有效,但对 eval() 或 setTimeout(string) 无效——后者需要额外禁用 unsafe-eval CSP 指令。安全不是加一道开关,而是每层都得对齐防护粒度。

















