最新下载
热门教程
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
Node.js怎样防止MongoDB执行注入式查询?
时间:2026-07-16 18:14:54 编辑:袖梨 来源:一聚教程网
MongoDB本身无SQL注入,但应用层将用户输入直接拼入查询对象会引发NoSQL注入;需对字段严格校验类型与格式,禁用$where、$regex等高危操作符,并实施四层纵深防护体系。
直接传req.body进findOne()就是高危操作
只要把用户输入原封不动塞进findOne()、find()或updateOne()的第一个参数,就等于把查询逻辑的控制权交给了攻击者。MongoDB驱动不做任何解析或拦截,它只负责把JavaScript对象序列化成BSON发给服务端——{ "$ne": null }会被照单执行,不是“无效语法”,而是合法查询指令。
常见错误现象:db.users.findOne({ username: req.body.username }),攻击者提交{"$ne": null},直接绕过登录校验;Model.find(req.query.filter),前端传{"status": {"$in": ["active", "deleted"]}}看似正常,但没人拦得住{"$where": "sleep(1000)"}。
- 所有主流驱动(Node.js原生驱动、Mongoose)行为一致:不校验、不重写、不拒绝非法结构
- Mongoose的
strict: true默认只限制写入时的schema外字段,对查询条件完全无感 - 别信“用了Mongoose就安全”——
Model.find(req.body)在任何版本下都等同于裸奔
对字符串字段必须做typeof + 正则双校验
username、email、title这类字段,业务上本就该是纯字符串,那就得用代码把它钉死。类型校验是第一道防线,格式校验是第二道,缺一不可。
- 先用
typeof value === 'string'排除对象、数组、null、undefined - 再用正则限定内容范围:
/^[a-zA-Z0-9_]{3,20}$/比/^.+$/安全得多 - 主动拒绝空字符串和前后空白:
value.trim() === ''需单独判断 - Unicode控制字符、零宽空格、BOM头也要过滤,可用
value.replace(/p{C}/gu, '')清理
示例:
if (typeof req.body.username !== 'string' || !/^[a-zA-Z0-9_]{3,20}$/.test(req.body.username.trim())) { return res.status(400).json({ error: 'Invalid username' });}
$regex、$where、$expr必须隔离用户输入
这些操作符是NoSQL注入的“特权通道”。哪怕前面做了严格的字符串校验,一旦放开它们接收原始输入,整个防御体系就失效。
-
$regex必须配合$options: 'i',且对用户输入做转义:req.query.q.replace(/[.*+?^${}()|[]]/g, '$&') -
$where应彻底禁用——它允许执行任意JavaScript,且无法走索引,性能和安全双崩 -
$expr若必须使用,只能接受白名单内的固定表达式,不能拼接用户输入 - 不要用
JSON.parse()二次解析用户输入——可能触发原型污染或正则DoS
错误写法:{ name: { $regex: req.query.q } };正确写法:
const escaped = req.query.q.replace(/[.*+?^${}()|[]]/g, '$&');db.users.find({ name: { $regex: escaped, $options: 'i' } });
别依赖“过滤$符号”或“黑名单操作符”
简单粗暴地删掉$或禁止$ne、$gt等关键词,是典型伪方案。MongoDB会安静忽略无效操作符,导致查询逻辑意外放行数据。
- 用户输入
{"status": {"$ne": "deleted"}},过滤后变成{"status": {"ne": "deleted"}},MongoDB不报错也不匹配,等价于{}——查出全部文档 - 攻击者可用Unicode编码绕过,比如
u0024ne代替$ne - 黑名单永远追不上新操作符,
$text、$geoWithin、$function(MongoDB 4.4+)都是潜在入口
真正有效的做法,是从源头切断恶意结构的传播路径:只允许你明确定义的字段名和值类型进入查询对象,其余一律拒之门外。复杂点在于字段语义各异——搜索字段要支持模糊匹配,ID字段要校验ObjectId格式,时间范围字段要转为Date实例。没有银弹,只有按字段逐个收口。
相关文章
- 王者荣耀世界零氪玩家如何生存 07-28
- 快手极速版怎么绑定手机号 07-28
- 明日方舟和轻松小熊联动活动内容一览 07-28
- 逆战未来黎明之光 逆战未来黎明之光玩法机制与新手入门指南 07-28
- 植物大战僵尸融合版毁灭土豆地雷介绍 07-28
- 逆战未来飓风之龙 逆战未来飓风之龙武器获取方法详解 07-28