json解析本身不导致sql注入,但解析后字段值若拼入sql语句则立即中招;必须全程使用参数化查询,动态字段名需白名单校验,输入验证应分层实施且不替代参数化。

JSON解析本身不产生SQL注入,但后续把解析出的字段值拼进SQL语句就会立刻中招——唯一安全做法是所有字段值进SQL前必须走参数化路径,不能出现在SQL字符串里。
为什么JSON.parse()之后还可能被注入
很多人误以为JSON校验=安全过滤,其实JSON.parse()只是把字符串转成JS对象,字段值仍是原始字符串,没做任何转义或类型约束。攻击者提交{"id": "1 OR 1=1"},后端若写"SELECT * FROM users WHERE id = " + data.id,数据库收到的就是SELECT * FROM users WHERE id = 1 OR 1=1。
常见错误场景包括:
- 用
JSON Schema校验了id是数字类型,但攻击者用"1; DROP TABLE logs;"绕过(Schema不校验内容语义) - 对
sort_by、filter这类“非核心字段”放松处理,结果它们被拼进ORDER BY或WHERE子句 - 前端传了嵌套结构如
{"user": {"name": "admin' --"}},后端直接取data.user.name拼SQL
Node.js中安全提取JSON字段的实操要点
关键不是“怎么解析JSON”,而是“解析后怎么用”。必须切断字符串拼接路径:
- 所有从
req.body取的值,进SQL前一律走参数化占位符:pool.query("SELECT * FROM t WHERE name = ?", [data.name])(MySQL)或pool.query("SELECT * FROM t WHERE name = $1", [data.name])(PostgreSQL) - 动态字段名(如
ORDER BY的列名)无法参数化,必须用白名单硬编码:const allowedSorts = ["created_at", "score"]; if (!allowedSorts.includes(data.sort_by)) throw new Error("Invalid sort field"); - 避免用
lodash.set或obj[key] = value动态赋值到查询对象,容易引入原型污染或字段覆盖风险
ORM场景下仍需警惕的危险操作
Sequelize、TypeORM等框架默认防注入,但开发者一不小心就掉坑里:
- 禁用
raw: true或literal:如User.findAll({ where: sequelize.literal(`name = '${data.name}'`) })直接回归拼接模式 - 避免
query()方法手写SQL:即使用了sequelize.query("SELECT * FROM users WHERE id = ?", { replacements: [data.id] })也比拼接强,但优先走模型方法 - 注意
include关联查询中的where条件,同样要确保传入的是变量而非字符串拼接结果
输入验证该放在哪一层
验证不是可选动作,但位置很关键:
- JSON Schema或Joi校验放在路由入口(如Express中间件),用于快速拦截明显非法结构(空值、超长、类型错),但它不替代参数化
- 字段长度、格式正则(如邮箱
/^[^\s@]+@[^\s@]+\.[^\s@]+$/)应在业务逻辑层做二次确认,防止绕过 - 不要在验证层做SQL转义(如
mysql.escape()),那会和参数化冲突,且易遗漏;转义只在极少数必须拼接的场景(如日志记录)中使用
最常被忽略的一点:JSON字段里的__proto__、constructor这类属性,如果被合并进查询对象,可能污染原型链并间接影响权限判断逻辑——解析后应先用Object.keys()或白名单过滤掉敏感键名。











