mongodb注入本质是应用层漏洞,非数据库自身缺陷;须在查询前对用户输入做类型校验与操作符白名单控制,禁用$where、$regex、$expr等高危操作符,并避免直接拼接req.body/query到find类方法中。

直接说结论:MongoDB注入不是数据库的问题,而是你把 req.body 或 req.query 未经校验就塞进 findOne()、find() 这类查询方法里导致的。只要做两件事——类型检查 + 操作符白名单,95% 的 NoSQL 注入就能拦住。
为什么 typeof req.body.username === 'object' 就该立刻拒绝
攻击者根本不用写 SQL,只用提交一个 JSON 对象就能改写整个查询逻辑。比如登录接口接收 {"$ne": null},findOne({ username: req.body.username }) 实际执行的是 { username: { "$ne": null } },结果绕过密码直接返回任意用户。
- 所有查询字段必须显式声明期望类型:
string、number、ObjectId,不能是object或array - 对字符串字段加正则限制,比如用户名用
/^[a-zA-Z0-9_]{3,20}$/,比/^.+$/安全得多 -
parseInt()、new ObjectId()这类强制转换必须配合isNaN()或ObjectId.isValid()校验,否则会静默转成null或非法 ID
哪些 MongoDB 操作符绝对不能放开给用户输入
$where、$regex、$expr 是 NoSQL 注入的“特权通道”,哪怕你前面做了类型检查,一开这三道门,防御就归零。
-
$where会执行 JavaScript,直接禁用:db.runCommand({ setParameter: 1, javascriptEnabled: false }) -
$regex必须转义 + 限定选项:req.query.q.replace(/[.*+?^${}()|[]\]/g, '\$&'),再拼进{ name: { $regex: escaped, $options: 'i' } } -
$expr允许聚合表达式,等同于开放数据库计算能力,生产环境应默认关闭,除非业务强依赖且输入经 Zod/Joi 严格约束
Mongoose 用户特别注意 schema.pre('find') 不管用
Mongoose 的 schema.pre('save') 只拦截写入,schema.pre('find') 默认不生效——它不会自动校验查询参数,也不会过滤掉 {"$ne": ""} 这种恶意结构。
- 6.0+ 版本开启
strictQuery: true(默认已启用),可自动剥离未知操作符,但不能替代显式校验 - 别信
req.body经过body-parser就安全了——它只解析 JSON,不验证内容合法性 - 真正有效的做法是:在路由 handler 开头用 Joi/Zod 做完整 schema 验证,错误直接
return res.status(400)
最常被忽略的一点:前端传来的 {"username": {"$ne": ""}} 在 Node.js 里已经是合法对象,typeof 判定为 object,但你的业务逻辑只打算接收字符串——这个类型鸿沟就是漏洞入口。校验必须发生在数据进入查询之前,而不是靠数据库或 ORM “帮忙兜底”。











