mongodb官方驱动不防注入,因其定位是协议层搬运工,不校验、不转义查询对象;防御必须在应用层实现,包括类型判断、操作符黑名单(如$ne、$regex)、深度遍历过滤及聚合管道校验。

官方驱动本身不提供“防注入过滤函数”——MongoDB 官方 Node.js 驱动(mongodb)从不解析、不校验、不转义你传入的查询对象,它只负责把 BSON 对象原样发给服务器。指望 findOne() 或 find() 自动过滤 $ne、$regex 是根本性误解。
为什么官方驱动不处理用户输入校验
MongoDB 驱动定位是“协议层搬运工”,不是“安全网关”。它假设你传进来的 { username: req.body.username } 已经是合法、可信、结构明确的 BSON 值。它不会检查 req.body.username 是字符串还是 {"$gt": ""},也不会拦截 $where —— 因为这些在 MongoDB 协议里都是合法操作符,禁用它们属于业务逻辑决策,不是驱动该干的事。
- 所有官方驱动(Node.js / Python / Java)都遵循这一设计哲学:最小干预、最大灵活性
- 如果你看到某个第三方库声称“自动防 NoSQL 注入”,它一定是在驱动之上加了中间层,而非驱动内置功能
- 误以为升级到最新版
mongodb驱动就能免疫注入,是线上事故高发原因
真正起作用的是 schema 层与运行时校验
防御必须落在应用代码里,且需分层落地:
-
mongoose的strict模式只约束save()写入,对find()查询完全无效;必须配合显式类型断言,例如:if (typeof req.query.id !== 'string') throw new Error('id must be string'); - 对每个查询字段做白名单式键名限制:允许
username、email,但拒绝任何以$开头的键(如$or、$expr),可用Object.keys(query).every(k => !k.startsWith('$')) - 对值做深度检测:递归遍历查询对象,遇到
object类型就检查是否含$键,遇到string就检查是否匹配/^$w+/(注意:Unicode 编码如u0024ne也要识别)
别碰 $regex 和 $where,真要支持就手动转义
这两个操作符是 NoSQL 注入的“特权入口”,绝大多数业务场景根本不需要开放:
-
$where在 MongoDB 5.0+ 默认禁用,但若旧配置残留或测试环境开启,db.runCommand({ setParameter: 1, javascriptEnabled: true })会瞬间复活风险 -
$regex必须配合严格转义:req.query.q.replace(/[.*+?^${}()|[]\]/g, '\$&'),再塞进{ name: { $regex: escaped, $options: 'i' } } - 绝对禁止让用户控制
$options值——'g'可能导致回溯爆炸,'x'开启注释模式等于主动引入语法漏洞
最常被忽略的一点:聚合管道(aggregate())比 find() 更危险。攻击者传入 [{ $match: { status: { $ne: null } } }] 看似无害,但若后端直接透传整个数组,就等于把查询控制权交出去了。防御必须覆盖所有接受用户输入构造查询的地方,包括 updateOne()、deleteMany()、countDocuments() —— 它们全都不做输入过滤。











