MongoDB本身不解析字符串查询,故无传统SQL注入,但应用层将用户输入直接拼入查询对象会引发NoSQL注入;攻击者可提交{"$ne": ""}等BSON结构篡改查询逻辑,需对字段做类型与格式校验,禁用$where、$regex等高危操作符。

直接说结论:MongoDB本身不解析字符串查询,所以没有传统意义上的“SQL注入”,但只要应用层把用户输入拼进查询对象(比如 findOne({username: req.body.username})),就极大概率触发NoSQL注入——这不是数据库漏洞,是代码写法漏洞。
为什么 req.body.username 直接进查询对象就是高危操作
因为MongoDB接受任意合法BSON结构作为查询条件,而攻击者可以提交类似 {"$ne": ""} 或 {"$regex": ".*"} 这样的JSON片段。一旦后端不做校验就原样传给 findOne() 或 find(),查询逻辑就被篡改了。
- 常见错误现象:登录接口输任意用户名+密码
{"$ne": null}就能绕过认证 - 使用场景:Node.js + Express + MongoDB 原生驱动、Mongoose 低版本(
6.0以前未启用严格模式)都容易中招 - 关键点:不是所有字段都该允许操作符;
username字段理应只接受字符串,但{"$ne": ""}是个对象,类型已失守
用 typeof 和正则做第一道过滤最简单有效
别迷信框架自动过滤。Mongoose 的 schema.pre('save') 只管写入,不管查询;Express 的 body-parser 更不会拦住恶意 JSON。
- 实操建议:对每个查询字段做显式类型+格式检查,例如:
if (typeof req.body.username !== 'string' || !/^[a-zA-Z0-9_]{3,20}$/.test(req.body.username)) { return res.status(400).json({ error: 'Invalid username' }); } - 参数差异:
/^[a-zA-Z0-9_]+$/比/^.+$/安全得多;空字符串、null、对象、数组必须被拒 - 容易踩的坑:用
JSON.parse()二次解析用户输入——这反而可能触发原型污染或 DoS,纯属多此一举
永远别让 $where、$regex、$expr 接收用户原始输入
这些操作符自带 JavaScript 执行能力或正则引擎,是NoSQL注入的“特权通道”。哪怕你做了类型检查,一旦放开它们,前面的防御就形同虚设。
- 常见错误现象:搜索功能用
{name: {$regex: req.query.q}},攻击者填.*或$(sleep(1000))(如果启用了 JS 执行) - 实操建议:
$regex必须配合$options: 'i'且对req.query.q做转义:const escaped = req.query.q.replace(/[.*+?^${}()|[]\]/g, '\$&'); db.users.find({ name: { $regex: escaped, $options: 'i' } }); - 性能影响:
$where会禁用索引,$regex以^开头才可能走索引;滥用等于主动给数据库拖后腿
真正难防的不是 {"$ne": 1} 这种一眼假的 payload,而是嵌套在深层对象里的操作符、被 Object.assign() 合并进去的污染字段、或者前端悄悄加上的 __proto__。防御的关键不是堵住某个符号,而是守住“用户输入 ≠ 查询结构”这条线。

















