直接拼接req.query.id是SQL注入漏洞源头,必须替换为参数化查询;mysql2/promise推荐用execute()而非query(),因前者默认预编译更安全,且参数必须为数组,表名/字段名等结构部分须白名单校验后硬编码。

直接拼接 req.query.id 就是漏洞源头
只要看到类似 const sql = `SELECT * FROM users WHERE id = ${req.query.id}` 这种写法,不管参数来源是 req.query、req.params 还是 req.body,都必须立刻替换。字符串拼接让输入内容直接变成 SQL 语法的一部分,攻击者输 1 OR 1=1 就能查出全表数据。
mysql2 的 query() 和 execute() 怎么选
推荐用 mysql2/promise 的 execute(),它默认启用预编译,安全性更高;query() 虽支持 ? 占位符,但不强制预编译,容易被绕过。
-
execute()必须传数组:哪怕只有一个参数,也得写成[req.query.id],不能是req.query.id -
query()允许传对象(如{ id: req.query.id }),但底层仍会转成数组,语义弱且易误用 - 批量插入时,
execute()对数组参数支持更稳,比如INSERT INTO t VALUES ?配[[a,b], [c,d]]
表名、字段名、ORDER BY 不能用占位符
? 和 $1 只能代入值,不能代入结构。把用户输入直接塞进 ORDER BY ${req.query.sort} 或 FROM ${req.query.table},照样中招。
- 字段名/表名必须走白名单校验:比如只允许
['id', 'name', 'created_at'],用if (!['id', 'name'].includes(req.query.sort)) throw new Error('Invalid sort') -
ORDER BY动态字段后,还得手动加反引号包裹:ORDER BY `${safeField}`,防止字段名含空格或关键字 - 多条件
WHERE拼接时,用express-validator提前过滤字段名和操作符,再组装 SQL 片段,而不是把整个WHERE当字符串拼
PostgreSQL 和 SQL Server 的参数写法差异别搞混
不同驱动对占位符语法要求严格,写错就报错或降级为字符串拼接。
- PostgreSQL(
pg)必须用$1、$2,顺序和数组一一对应;写?直接抛bind message supplies 1 parameters, but prepared statement "" requires 0 - SQL Server(
mssql)推荐命名参数@id,但要用ps.prepare()+ps.execute({ id: ... })流程,漏掉ps.unprepare()会导致连接池泄漏 - 所有数据库驱动都会拒绝含
--、/*的参数名,但仅限命名参数场景;值参数仍需靠占位符机制隔离,不能依赖这个检测
真正难的不是改一行 query(),而是确保日志里没打印原始 SQL 字符串、错误响应里没暴露查询模板、所有 if/else 分支里的 SQL 构造都统一用了参数化——漏掉任意一处,等于没修。

















