模板字符串本身不防SQL注入,${id}是字符串拼接而非参数化;sql-template-strings通过分离text与values,强制交由数据库驱动执行预编译查询,杜绝注入风险。

模板字符串本身不防SQL注入,别被语法迷惑
JavaScript 的 \`SELECT * FROM users WHERE id = ${id}\` 看起来像“参数化”,但本质仍是字符串拼接——${id} 会被直接求值并转为字符串插入,没有任何转义或类型约束。只要 id 来自用户输入(比如 URL 参数、表单字段),攻击者传入 1 OR 1=1 -- 就能绕过条件,甚至执行任意 SQL。
用 sql-template-strings 不是为了“美化写法”,而是把它当做一个轻量的、运行时强制参数分离的契约工具:它把查询结构和数据彻底拆开,让数据库驱动(如 pg、mysql2)真正拿到的是预编译语句 + 独立参数数组,而非拼好的 SQL 字符串。
安装与基础用法:必须配合真正的参数化驱动
sql-template-strings 本身不执行 SQL,也不连接数据库。它只做一件事:把模板字面量解析成 { text: string, values: any[] } 对象。你仍需把结果交给支持参数化查询的驱动(例如 pg.query() 或 mysql2.execute())。
- 安装:
npm install sql-template-strings - 导入:
import { sql } from 'sql-template-strings';(ESM)或const { sql } = require('sql-template-strings');(CommonJS) - 正确用法示例(PostgreSQL + pg):
const { sql } = require('sql-template-strings'); const { Client } = require('pg'); <p>const client = new Client(); await client.connect();</p><p>const userId = req.query.id; // 来自用户 const query = sql`SELECT * FROM users WHERE id = \${userId} AND status = \${'active'}`;</p><p>// query.text → 'SELECT * FROM users WHERE id = $1 AND status = $2' // query.values → [req.query.id, 'active']</p><p>const res = await client.query(query); // pg 自动绑定 $1, $2
立即学习“Java免费学习笔记(深入)”;
Java Maven 项目审查工作流的通用输入处理。当 Java Maven 项目以 ZIP压缩包或 GitLab 仓库 URL 形式提供且需要...时使用。
常见错误:混用字符串拼接和 sql 模板
一旦在 sql\`...\` 内部手动拼接变量(比如用 + 或 \${... + ...}),就等于绕过了整个防护机制。以下写法完全无效,且更危险:
-
sql\`SELECT * FROM users WHERE name = '\${name.replace(/'/g, "''")}'\`—— 手动转义不可靠,且破坏了参数化语义 -
sql\`SELECT * FROM \${tableName} WHERE id = \${id}\`—— 表名无法参数化,sql-template-strings不处理它;应白名单校验tableName,再拼进模板外层 -
sql\`SELECT * FROM users WHERE id = \${id || 0}\`—— 默认值逻辑若含用户输入,仍可能引入漏洞;应在进入sql前做类型转换和校验
为什么不用正则替换或手写 escape 函数?
SQL 注入的上下文千变万化:字符串字面量、数字、布尔、数组、JSON 字段、ORDER BY 列名……每种都需要不同规则。正则无法覆盖所有 SQL 解析边界(比如嵌套引号、注释、Unicode 分隔符)。而 sql-template-strings 的价值在于它不尝试“修复”字符串,而是把控制权交给数据库驱动——后者知道如何为当前方言安全地序列化每个 query.values[i]。
它不解决动态表名、列名、排序方向(ASC/DESC)这类元数据问题。那些必须走白名单或严格 schema 映射,不能塞进 ${}。
真正容易被忽略的一点:如果你用的是 ORM(如 Prisma、TypeORM),它们内部已做参数化,此时再套一层 sql-template-strings 不仅多余,还可能干扰其查询构建逻辑。只在直连原生驱动、又想保留模板可读性时才需要它。

















