文章总结: CVE-2026-29793是FeathersJS框架@feathersjs/mongodb适配器中的一处NoSQL注入漏洞。因WebSocket传输未校验id参数类型,攻击者可传入操作符对象绕过验证,造成未授权数据读写。影响版本5.0.0至5.0.41。建议立即升级至5.0.42,或在服务端钩子中增加类型校验。文章揭示了多协议框架中传输层隐式信任差异带来的风险。 综合评分: 95 文章分类: 漏洞分析,漏洞预警,WEB安全
CVE-2026-29793:一枚藏在 WebSocket 里的 NoSQL 注入
原创
CVE-SEC CVE-SEC
CVE-SEC
2026年3月12日 10:00 四川
CVE-2026-29793:一枚藏在 WebSocket 里的 NoSQL 注入
披露时间:2026-03-10 CVSS 4.0:9.3 / CRITICAL 影响组件:@feathersjs/mongodb 5.0.0 – 5.0.41
背景
FeathersJS 是 Node.js 生态中一个相当流行的实时应用框架,支持同时以 REST 和 WebSocket(Socket.IO)两种协议暴露服务接口,后端数据库可以对接 MongoDB、PostgreSQL 等多种数据源。在使用 MongoDB 时,官方提供了 @feathersjs/mongodb 适配器作为标准数据访问层。
2026 年 3 月初,一名安全研究员向 FeathersJS 维护团队提交了一份协调披露报告,指出该适配器在处理 WebSocket 传输路径上存在一处输入验证缺失,可被未授权攻击者利用,直接读取、篡改或删除数据库中的任意记录。这就是本文要分析的 CVE-2026-29793。
漏洞是怎么来的
理解这个漏洞,需要先了解一个背景差异:REST 调用和 WebSocket 调用在参数类型上并不等价。
当你通过 HTTP REST 请求访问一个资源时,id 来自 URL 路径,例如 GET /users/abc123,路由层天然将其解析为字符串,后端收到的永远是字符串类型。
而通过 Socket.IO 调用时,参数是以 JSON 序列化的 JavaScript 值直接传递的,客户端可以传入任意合法的 JSON 结构,包括嵌套对象。换句话说,攻击者完全可以把 id 设成 {"$ne": null} 这样的东西传给服务端。
问题就出在 @feathersjs/mongodb 适配器的 getObjectId() 函数上。在 5.0.41 及之前版本,它的处理逻辑简化来说是这样的:
async _get(id, params) {
const query = { [this.id]: this.getObjectId(id) }
const data = await this.Model.findOne(query)
// ...
}
getObjectId() 只负责尝试把值转成 MongoDB ObjectId,但它没有拒绝传入对象类型的能力。当 id 是 {"$ne": null} 时,函数原样返回这个对象,最终构造出的 MongoDB 查询变成了:
db.collection('users').findOne({ _id: { $ne: null } })
在 MongoDB 的查询语义里,$ne: null 表示”字段值不为 null”,这个条件几乎匹配集合里的全部文档。结果就是:攻击者不需要知道任何合法的用户 ID,一条消息就能拿到数据库里的任意记录。
问题有多早
有意思的是,类似的担忧早在 2015 年就出现在 FeathersJS 的 GitHub Issue 讨论(#113)中,当时有开发者指出框架对 Socket.IO 畸形消息的处理太过宽松,容易导致服务崩溃,建议在传输层增加参数合法性验证。这个建议在后来的版本迭代中始终没有被系统性地落实到 id 参数的类型校验上,直到这次 CVE 才触发了真正的修复。
值得单独提一下 v5.0.41 版本的情况。该版本的变更日志写着”all client methods require valid ids”,看起来像是修复了这个问题,但实际上这个校验加在了 FeathersJS 官方 JavaScript 客户端一侧,而不是服务端适配器一侧。任何直接操作 Socket.IO 协议的攻击者,完全不会经过这层客户端校验,漏洞依然存在。
时间线
| 时间 | 事件 | | — | — | | 2024-08-12 | FeathersJS v5.0.0 发布,漏洞代码路径随 @feathersjs/mongodb 适配器引入 | | 2026-02-19 | v5.0.41 发布,客户端侧新增 id 合法性校验,服务端适配器层缺陷未修复 | | 2026-03-04 | 安全研究员私下报告漏洞,维护者 David Luecke 当日提交修复 PR #3664(commit 681a3bf),v5.0.42 发布 | | 2026-03-10 | GitHub Security Advisory GHSA-p9xr-7p9p-gpqx 公开,CVE-2026-29793 正式分配并同步至 NVD |
影响范围
受影响的包是 @feathersjs/mongodb,版本区间 5.0.0 至 5.0.41,覆盖整个 v5 主干的绝大部分生命周期。
需要三个条件同时满足才能触发:
- 应用使用
@feathersjs/mongodb5.0.0–5.0.41 作为数据库适配器; - 应用注册了
@feathersjs/socketio,通过 Socket.IO 对外暴露服务方法; - 没有在
before钩子中对id参数额外做类型校验。
仅使用 HTTP REST 传输而不启用 Socket.IO 的部署不受此漏洞影响。
从 npm API 统计来看,@feathersjs/mongodb 在 2026 年 2 月初至 3 月初约一个月内的下载量约为 20,156 次,日均 650 次左右,使用规模在中等体量的 Node.js 生态组件中属于活跃水平。
攻击链全过程
攻击者只需要能访问目标服务的 Socket.IO 端点,不需要任何账户和凭证。完整的攻击路径如下:
攻击者
|
| 1. 发起 HTTP 请求完成 Socket.IO 握手
v
Socket.IO 传输层
|
| 2. 升级为 WebSocket 连接
v
@feathersjs/socketio 传输适配器
|
| 3. 接收事件,映射到服务方法调用
| socket.emit('get', 'users', {$ne: null}, {})
v
FeathersJS Service Layer(before hooks 无类型校验)
|
| 4. id 参数原样透传
v
@feathersjs/mongodb MongoDbAdapter._get()
|
| 5. getObjectId({$ne: null}) 未抛异常,原样返回对象
v
MongoDB Node.js Driver
|
| 6. 执行查询 db.users.findOne({ _id: { $ne: null } })
v
攻击者拿到未授权的用户数据
除了 get 读取,remove、patch、update 也可以用同样的方式传入操作符对象,分别对应批量删除和批量篡改。如果服务开启了 multi: true,一次 remove 调用就能清空整张集合表。
触发注入的操作符不止 $ne: null 一种,$gt: ""、$exists: true、$regex: "^" 等均可绕过 id 校验,修复版本的测试用例也专门覆盖了 $regex 这类变种。
修复细节
v5.0.42 的修复直接、干净。PR #3664(commit 681a3bf)在 adapter.ts 的方法入口处增加了三行类型断言:
if (typeof id !== 'string' && typeof id !== 'number' && !(id instanceof ObjectId)) {
throw new BadRequest(`Invalid id '${JSON.stringify(id)}'`)
}
合法的 id 类型被限定为字符串、数字或 MongoDB ObjectId 实例,任何传入的普通对象一律以 400 BadRequest 拒绝,不会进入查询构造逻辑。
与此同时,PR #3665 并行修复了 _patch 方法中 data 参数的操作符注入问题(攻击者可传入 $set、$unset 等更新操作符)。这两个 PR 共同构成了本次安全加固专项的完整修复。
需要说明的是,这次修复只覆盖了 id 参数这条路径。find 方法中通过 query 参数传入 MongoDB 操作符的问题(例如 { password: { $regex: "^admin" } })属于独立的攻击面,需要通过 Feathers schema 校验或 sanitizeQuery 钩子单独处理,不在本次修复范围内。
如何判断自己是否受影响
在项目目录下运行:
npm list @feathersjs/mongodb
如果输出的版本号在 5.0.0 到 5.0.41 之间,且应用同时启用了 Socket.IO 传输,则需要立即处理。
修复与缓解
直接升级是唯一推荐的解法:
npm install @feathersjs/mongodb@latest
如果因为某些原因无法立即升级,可以在所有涉及 id 参数的服务方法 before 钩子里加入类型校验,作为临时缓解:
const { BadRequest } = require('@feathersjs/errors')
const { ObjectId } = require('mongodb')
const validateId = async (context) => {
const { id } = context
if (id !== null && id !== undefined) {
if (typeof id !== 'string' && typeof id !== 'number' && !(id instanceof ObjectId)) {
throw new BadRequest('Invalid id parameter type')
}
}
return context
}
在网络层,如果业务上不需要对外开放 Socket.IO,建议通过防火墙或反向代理限制 WebSocket 端口的公网访问范围。
检测思路
从流量侧,监控 WebSocket 帧载荷中是否出现 "$ne"、"$gt"、"$exists"、"$regex" 等 MongoDB 操作符字符串是最直接的方式。Socket.IO v4 的事件帧以 42[ 开头,可以作为流量过滤的辅助标志。
从应用侧,在 FeathersJS 的 before 钩子里记录 id 参数为对象类型的调用,可以有效捕捉异常请求。
一点延伸思考
这个漏洞有一个值得关注的模式:同一套业务逻辑,通过 REST 调用是安全的,通过 WebSocket 调用就出了问题。根因在于两条传输路径在参数类型上的隐式差异,而适配器层对这个差异毫无意识。
这种”传输协议隐式信任差异”在多协议框架里并不罕见。REST 路由层对路径参数的字符串化是隐性的类型约束,开发者已经习以为常,但当同一框架支持 WebSocket 时,这层隐性约束就消失了,如果适配器没有显式补上,盲区就出现了。
从防御角度来看,凡是同时支持 REST 和 WebSocket 的应用层框架,都应该在服务端适配器层对所有外部传入的参数做显式的类型校验,不能依赖传输协议的隐性行为来保证安全性。
参考资料
- GitHub Security Advisory GHSA-p9xr-7p9p-gpqx:https://github.com/feathersjs/feathers/security/advisories/GHSA-p9xr-7p9p-gpqx
- NVD CVE-2026-29793:https://nvd.nist.gov/vuln/detail/CVE-2026-29793
- 修复 PR #3664:https://github.com/feathersjs/feathers/pull/3664
- 修复 PR #3665:https://github.com/feathersjs/feathers/pull/3665
- FeathersJS v5.0.42 Release Notes:https://github.com/feathersjs/feathers/releases/tag/v5.0.42
- CWE-943:https://cwe.mitre.org/data/definitions/943.html
免责声明:
本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。
任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。
本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我。
本文转载自:CVE-SEC CVE-SEC CVE-SEC《CVE-2026-29793:一枚藏在 WebSocket 里的 NoSQL 注入》
版权声明
本站仅做备份收录,仅供研究与教学参考之用。
读者将信息用于其他用途的,全部法律及连带责任由读者自行承担,本站不承担任何责任。










评论