唯一可靠防护是全程使用参数化查询:mysql2用?占位符(需数组传参),PostgreSQL用$1形式;表名/字段名等结构部分须白名单校验后硬编码,不可参数化;日志与错误响应中禁止输出原始SQL或敏感输入。

Express 本身不提供 SQL 注入防护,所有防护必须由开发者主动引入和配置——靠 req.body 或 req.query 直接拼接 SQL 字符串,100% 中招。
为什么 express-sql-sanitizer 不是可靠方案
它只做字符串替换(比如把 ' 换成 ''),对绕过手法完全无效,例如 1; DROP TABLE users-- 或 Unicode 编码变体。这类“消毒”中间件在真实攻击场景中形同虚设。
- 它无法识别参数化查询上下文,删掉分号或注释符反而可能破坏合法输入
- 不处理
ORDER BY、UNION SELECT等动态结构字段名/表名注入点 - 与 ORM(如 Prisma、Knex)或原生驱动(如
pg、mysql2)的参数化机制冲突
真正有效的防护只有一种:全程使用参数化查询
不是“加个中间件”,而是从数据库调用层彻底切断字符串拼接路径。所有用户输入必须通过占位符传入,由驱动底层完成转义。
- 用
mysql2时,写connection.execute('SELECT * FROM users WHERE id = ?', [req.params.id]),而不是WHERE id = ' + req.params.id - 用
pg时,必须用$1,$2占位符:client.query('SELECT * FROM logs WHERE level = $1', [req.query.level]) - ORM 如 Prisma 不允许原始 SQL 拼接,但若用了
$queryRaw,仍需手动校验字段白名单,不能直接插req.query.sort
动态字段名/表名等无法参数化的场景怎么处理
参数化查询不支持字段名、表名、排序方向(ASC/DESC)等结构部分,这里必须用白名单硬控制,而非正则过滤或黑名单。
- 排序字段:定义允许列表
const allowedSortFields = ['created_at', 'name', 'status'],再校验req.query.sort是否在其中 - 排序方向:只接受
req.query.order === 'ASC' || req.query.order === 'DESC',其余一律拒绝 - 多表联查或动态表名:避免运行时拼接;改用预定义视图、固定路由或配置驱动的查询模板
最常被忽略的是日志记录和错误响应——把原始 SQL 或 req.query 直接打到日志里,等于把攻击载荷存档;返回 500 时暴露数据库错误详情(如 column "xxx" does not exist),也会辅助攻击者探测结构。这两处不处理,前面所有参数化都可能被绕过。


















