一聚教程网:一个值得你收藏的教程网站

最新下载

热门教程

如何利用标记模板字符串(Tagged Templates)防御 SQL 注入与 XSS

时间:2026-08-04 09:58:49 编辑:袖梨 来源:一聚教程网

如何利用标记模板字符串(Tagged Templates)防御 SQL 注入与 XSS需要先看清适用场景和关键步骤,避免只记结论却忽略实际限制。

模板字符串本身不防SQL/XSS,因${}仅作字符串拼接无转义;真正安全需自定义标签函数,如sql...${x}...生成?占位符与参数数组交由驱动执行。

为什么 String.raw 或普通模板字符串本身不防 SQL/XSS

很多人误以为用了模板字符串就“天然安全”,其实完全相反:`SELECT * FROM users WHERE id = ${id}` 和拼接字符串一样危险。JavaScript 模板字符串只是语法糖,${} 里的值会直接转成字符串并插入,没有任何转义或上下文感知。SQL 注入和 XSS 的本质是“数据被当成了代码执行”,而模板字符串根本不区分数据与代码——它只负责拼。

Tagged Templates 怎么介入并起作用

真正起作用的是标签函数(tag function),它能拦截模板字面量和插值参数,拿到原始字符串数组和动态值,从而在拼接前做上下文相关的处理。关键点在于:你必须自己写/用可靠的标签函数,且它得知道当前目标是 SQL 还是 HTML。

  1. SQL 场景下,标签函数要对每个插值参数做参数化处理(如转成 ? 占位符),再把值单独传给数据库驱动,绝不能拼进字符串
  2. HTML 场景下,标签函数要对每个插值做 HTML 实体编码(如 &&),但注意:不能编码整个模板字符串,否则破坏结构;只编码插值部分
  3. 标签函数无法自动识别意图——sql`...${x}...`html`<div>${x}</div>` 的区别全靠函数名和实现逻辑,不是语法强制的

实际可用的标签函数怎么写(以 SQL 为例)

下面这个 sql 标签函数不执行查询,只生成带 ? 占位符的语句和参数数组,交由底层驱动(如 mysql2pg)安全执行:

function sql(parts, ...values) {  let query = parts[0];  const params = [];  for (let i = 0; i < values.length; i++) {    query += '?' + parts[i + 1];    params.push(values[i]);  }  return { text: query, params };}<p>// 使用示例const id = "1; DROP TABLE users; --";const result = sql<code>SELECT * FROM users WHERE id = ${id} AND active = ${true}</code>;// → { text: "SELECT * FROM users WHERE id = ? AND active = ?", params: ["1; DROP TABLE users; --", true] }

注意:result 不能直接传给 exec(),必须交给支持参数化查询的驱动。若你硬用 eval() 或字符串拼接去“执行” result.text,等于白做。

常见陷阱和不可替代的前提

Tagged Templates 不是银弹。最容易被忽略的几点:

  1. 你写的标签函数如果内部调用了 String(value) 再拼进 SQL 字符串,就立刻失效——必须坚持参数分离,绝不拼值
  2. HTML 场景下,如果插值本身是已信任的 HTML 片段(比如组件返回的 <span>OK</span>),用 html 标签函数会把它双编码成 <span>OK</span>,反而显示为源码。这时需要显式标记“已信任”,例如 html`<div>${raw(trustedHtml)}</div>`
  3. 所有插值都必须走标签函数路径。漏掉一个 ${userInput} 没包在 sql``html`` 里,整条链就破了
  4. 后端模板引擎(如 EJS、Nunjucks)的插值语法()和 JS Tagged Templates 无关,别混用

真正的防线不在语法糖,而在是否始终把数据和代码的边界守死——Tagged Templates 只是帮你把这道边界显式地写在调用处。

热门栏目