唯一可靠的方式是参数化查询,其他都是补救手段;必须使用?占位符传递值,表名列名等动态标识符须白名单校验,数据库账号权限需最小化,错误信息严禁暴露给前端。

唯一可靠的方式是参数化查询,其他都是补救手段。不走这条路,迟早出事。
connection.query() 的 ? 占位符必须用,不能省
Node.js 里用 mysql2 或官方 mysql 包时,connection.query() 支持 ? 占位符,这是值安全注入的唯一通道。它不是“可选优化”,而是强制要求。
- ✅ 正确:
connection.query('SELECT * FROM users WHERE id = ?', [userId], callback) - ❌ 危险:
connection.query('SELECT * FROM users WHERE id = ' + userId)—— 即使userId是数字,也别拼 - ⚠️ 注意:
?只保护值,对表名、列名、ORDER BY字段无效;这些地方拼接会直接报错或被绕过
表名、列名、排序字段必须白名单校验,escapeId() 是备选
动态 SQL 中无法用 ? 的部分(比如按月份分表、多租户切换 schema、前端传来的排序字段),不能靠转义兜底,得靠业务层硬控制。
PigX UI Pro 前端开发指南 - Vue 3 + TypeScript + Element Plus。当用户提到 PigX UI、PigX 前端、lgb-mgui 项目、Vue 3 企业级后台开发、Element Plus 后台开发时使用此技能。
- ✅ 推荐:提前定义允许的字段列表,例如
const allowedSortFields = ['created_at', 'status', 'name'];,运行时校验输入是否在其中 - ✅ 备选:
connection.escapeId(tableName)会加反引号并转义内部反引号,能防user`; DROP TABLE users; --这类 payload,但不如白名单可靠 - ❌ 禁止:
connection.escape()用于表名——它只处理字符串值,对标识符完全无效 - ⚠️
ORDER BY ?在 MySQL 中语法错误,必须用白名单映射成实际字段名再拼
数据库账号权限必须最小化,root 账号上线等于裸奔
即使所有 SQL 都用了参数化,只要数据库账号有高危权限,攻击者拿到执行入口后就能读文件、删库、写 WebShell。
- ✅ 应授予:
SELECT、INSERT、UPDATE、DELETE(按需),禁用GRANT、ALTER、DROP、FILE、EXECUTE - ✅ 检查命令:
SHOW GRANTS FOR 'app_user'@'%';,确认输出里没出现上述高危权限 - ❌ 常见错误:开发环境用
root,上线忘记换;或一个账号通吃所有库
错误信息绝不能返回给前端,哪怕只是字段名
MySQL 默认错误如 Unknown column 'xxx' in 'where clause' 或 You have an error in your SQL syntax,会暴露表结构、字段名甚至拼接逻辑,帮攻击者反推 SQL 模板。
- ✅ 必做:在
connection.query()的回调里捕获err,只返回通用提示(如“请求失败”),完整错误记入服务端日志 - ❌ 绝对禁止:
res.json({ error: err.message })—— 这等于把数据库拓扑图打包送给攻击者 - ⚠️ 补充:Node.js 中建议设
options.multipleStatements: false(默认就是 false),避免因开启多语句执行而放大风险
最易被忽略的是动态标识符处理和错误信息控制——前者常被当成“小问题”随便拼,后者常因调试方便而放行。这两处一旦松动,参数化就形同虚设。










