Mongoose NoSQL 注入绕过,9.11.4 已修复(CVE-2026-125120)
Mongoose 未一致应用 sanitizeFilter,攻击者控制的过滤器会到达 countDocuments 和连接查询;9.11.4 已修复。
TL;DR
- Mongoose 的
sanitizeFilter防御未应用于countDocuments,在连接上设置时会被忽略,且三条代码路径会复制继承属性。攻击者控制的过滤器可以绕过授权、改变计数或使查询崩溃。 - Mongoose 6.13.12、7.8.13、8.24.7、9.11.4 之前的四个支持系列均受影响。
countDocuments缺陷为 CVE-2026-125120,评为 CVSS v3.1 9.8 严重。 - 未发现实际利用。公告包含概念验证代码,截至 2026 年 11 月 11 日,这些缺陷均未列入 CISA 的 Known Exploited Vulnerabilities 目录。
- 更新到 6.13.12、7.8.13、8.24.7 或 9.11.4。如果依赖
sanitizeFilter,在更新所有服务之前添加公告中的countDocuments中间件。参见“如何修复?”。
我受影响吗?
必须同时满足以下条件:
- 应用使用 Mongoose,并将攻击者控制的值传入过滤器、更新或文档构建。
- 应用依赖
sanitizeFilter进行防护。可以是全局mongoose.set('sanitizeFilter', true)、连接选项或每次查询的选项。 - 对于严重缺陷,应用使用包含攻击者控制值的过滤器调用
countDocuments。
另外两个公告扩大了条件。连接缺陷影响使用 mongoose.createConnection(uri, { sanitizeFilter: true }) 设置 sanitizeFilter 的应用。继承属性缺陷影响使用攻击者控制的字段名加载文档或设置点路径的应用,即使没有 sanitizeFilter 也适用。
Mongoose 是 Node.js 广泛使用的 MongoDB 库。npm 注册表在截至 2026 年 11 月 9 日的一周记录了 mongoose 软件包 8,868,838 次下载,GitHub 仓库有 27,449 个星标。
| 系列 | 受影响版本 | 修复版本 |
|---|---|---|
| 9.x | 9.0.0 至 9.11.3 | 9.11.4,2026-10-23 发布 |
| 8.x | 8.0.0 至 8.24.6 | 8.24.7,2026-10-23 发布 |
| 7.x | 7.0.0 至 7.8.12 | 7.8.13 |
| 6.x | 6.0.0 至 6.13.11 | 6.13.12 |
表中使用三个公告中最高的修复下限。仅 countDocuments 缺陷于 2026 年 8 月 10 日在 9.9.2、8.24.3、7.8.12 和 6.13.11 中修复。连接和继承属性的修复随后在 9.11.4、8.24.7、7.8.13 和 6.13.12 中发布。只安装 8 月版本会留下两个 High 缺陷。
检查已安装版本:
npm ls mongoose
grep -A2 '"node_modules/mongoose"' package-lock.json
yarn why mongoose
pnpm list mongoose
然后查找防御配置的位置:
grep -R "sanitizeFilter" --include='*.js' --include='*.ts' .
使用 { sanitizeFilter: true } 创建的连接会暴露于 CVE-2026-125119,即使 connection.get('sanitizeFilter') 返回 true。
如何修复?
更新到主系列的修复版本,并重新部署所有使用 Mongoose 的服务。
- 在 Mongoose 9.x 上安装 9.11.4 或更高版本:
npm install mongoose@9.11.4 - 在 Mongoose 8.x 上安装 8.24.7 或更高版本:
npm install mongoose@8.24.7 - 在 Mongoose 7.x 上安装 7.8.13 或更高版本:
npm install mongoose@7.8.13 - 在 Mongoose 6.x 上安装 6.13.12 或更高版本:
npm install mongoose@6.13.12 - 重新构建并重新部署每个服务,然后针对新版本运行认证和基于计数的授权测试。
如果无法立即升级,countDocuments 公告发布了中间件临时方案。它在计数执行前应用 sanitizeFilter:
mongoose.plugin(function() {
schema.pre('countDocuments', function() {
// This will always apply `sanitizeFilter` on `countDocuments`
mongoose.sanitizeFilter(this.getFilter());
});
});
对于连接缺陷,不要依赖 createConnection(uri, { sanitizeFilter: true })。使用全局 mongoose.set('sanitizeFilter', true) 或每次查询的 .sanitizeFilter()。该临时方案不能修复存储的继承属性缺陷;只有升级才能修复。如果无法升级,请验证应用接受的字段名,并拒绝 toString、valueOf、constructor 和 $__schema 等路径。
根本原因是什么?
Mongoose 的 sanitizeFilter 将过滤器值中的操作符对象包裹在 $eq 中,使 { $ne: null } 这样的值按字面比较,而不是被解释为 MongoDB 操作符。只有当每条查询路径都应用它时,防御才有效。这三个公告是这一承诺的三个缺口。
第一个缺口是 countDocuments。在 8.24.2 中,_countDocuments 直接转换条件,而没有经过应用 sanitizeFilter 的 _castConditions:
Query.prototype._countDocuments = async function _countDocuments() {
this._applyTranslateAliases();
try {
this.cast(this.model);
} catch (err) {
this.error(err);
}
_castConditions 检查配置的选项并调用 sanitizeFilter(this._conditions),但 countDocuments 从未到达它。修复让该方法调用 _castConditions()。
第二个缺口是连接选项。NativeConnection#createClient() 将 sanitizeFilter 从连接选项移到连接的 config 对象,并从 options 中删除:
if ('sanitizeFilter' in options) {
this.config.sanitizeFilter = options.sanitizeFilter;
delete options.sanitizeFilter;
}
8.24.6 中的 Query.prototype._castConditions 读取 this.model?.db.options?.sanitizeFilter,然后 this.model?.base.options?.sanitizeFilter,然后每次查询的选项。它从不读取 this.model.db.config.sanitizeFilter,因此即使 connection.get('sanitizeFilter') 返回 true,连接选项也会被静默忽略。
第三个缺口是继承属性。Mongoose 阻止 __proto__、constructor 和 prototype,但其文档水合和路径设置代码不检查属性是否为对象自身所有。在 init() 中,名为 toString 的存储字段解析为继承的 Object.prototype.toString,其为真值,因此 Mongoose 递归进入该函数并将存储的子字段复制到其上:
// assume nested object
if (!doc[i]) {
doc[i] = {};
if (!strict && !(i in docSchema.tree) && !(i in docSchema.methods) && !(i in docSchema.virtuals)) {
self[i] = doc[i];
} else if (opts?.virtuals && (i in docSchema.virtuals)) {
self[i] = doc[i];
}
}
init(self, value, doc[i], opts, path + '.');
处理点路径的 $__set 和合并更新文档的 utils.mergeClone 也缺少相同的自有属性检查。修复在写入前添加 Object.hasOwn 检查。
如何被利用?
- 对于 CVE-2026-125120,应用在启用
sanitizeFilter时调用Model.countDocuments({ username: req.body.username, password: req.body.password })。攻击者发送{"password": {"$ne": null}}。 - 由于
countDocuments跳过了_castConditions,操作符对象作为操作符到达 MongoDB。即使密码错误,计数也会返回匹配的账户。 - 将非零计数视为成功的认证或配额检查会授予访问权限,或返回攻击者不应看到的数据。
- 对于 CVE-2026-125119,应用使用
sanitizeFilter: true创建连接,并将{"password": {"$ne": null}}传入findOne。该选项位于Connection.config,而不是Connection.options,因此过滤器未被清理,查询返回第一个账户。 - 对于 CVE-2026-125117,攻击者存储或提交名为
toString的字段,值为{"call": 1}。水合将该值复制到共享函数上,进程中后续所有查询都会抛出TypeError: _toString.call is not a function。 - 同一缺口还可到达子文档的共享模式,例如将
profile.$__schema.paths.email.validators设为空数组,从而删除进程中所有文档的required验证器。
公告包含连接和继承属性缺陷的概念验证代码。严重公告包含一个回归测试,显示修复后操作符被包裹在 $eq 中。未报告实际利用。
攻击如何展开?
攻击者控制的操作符对象到达跳过了清理器的查询路径,MongoDB 将其解释为操作符,返回应用不打算返回的数据或改变状态。
例如 { $ne: null }"] --> B["应用将其传给
countDocuments 或连接查询"] B --> C["sanitizeFilter 被跳过
或存入错误的选项"] C --> D["MongoDB 将该值
当作操作符"] D --> E["查询返回记录
或应用信任的计数"] E --> F["认证绕过
或数据暴露"]
攻击者能做什么?
影响取决于路径。countDocuments 缺陷允许攻击者改变应用用于授权、分页、配额或业务逻辑的计数结果。连接缺陷允许攻击者绕过密码检查并读取账户记录。继承属性缺陷允许攻击者覆盖共享的内置函数并造成拒绝服务,或更改共享模式验证器。
条件为:
- 应用将攻击者控制的对象传入 Mongoose 过滤器、更新或文档构建。
- 对于严重缺陷,应用使用
countDocuments并依赖sanitizeFilter。 - 对于连接缺陷,应用在
createConnection上设置sanitizeFilter,而不是全局或每次查询。 - 对于继承属性缺陷,应用接受攻击者控制的字段名或点路径,或存储字段名由攻击者控制的文档。
公告描述数据泄露和拒绝服务。它们未描述远程代码执行,也没有来源证明这些路径可执行代码。
为什么修复有效?
修复在准备过滤器时关闭每个缺口。对于 countDocuments,该方法现在调用 _castConditions,因此在查询到达 MongoDB 之前运行 sanitizeFilter:
Query.prototype._countDocuments = async function _countDocuments() {
this._applyTranslateAliases();
-
- try {
- this.cast(this.model);
- } catch (err) {
- this.error(err);
- }
+ this._castConditions();
if (this.error()) {
throw this.error();
对于连接选项,_castConditions 现在首先读取连接的 config:
Query.prototype._castConditions = function() {
- let sanitizeFilterOpt = undefined;
- if (this.model?.db.options?.sanitizeFilter != null) {
- sanitizeFilterOpt = this.model.db.options.sanitizeFilter;
- } else if (this.model?.base.options?.sanitizeFilter != null) {
- sanitizeFilterOpt = this.model.base.options.sanitizeFilter;
- } else {
- sanitizeFilterOpt = this._mongooseOptions.sanitizeFilter;
- }
+ const sanitizeFilterOpt = this.model?.db.config?.sanitizeFilter ??
+ this.model?.db.options?.sanitizeFilter ??
+ this.model?.base.options.sanitizeFilter ??
+ this._mongooseOptions.sanitizeFilter;
对于继承属性,文档代码在写入前检查 Object.hasOwn:
// assume nested object
+ // Skip inherited properties like `Object.prototype.toString`
+ // to avoid setting properties on shared objects
+ if (!Object.hasOwn(doc, i) && i in doc) {
+ continue;
+ }
if (!doc[i]) {
doc[i] = {};
相同的检查也进入点路径设置器和合并辅助函数。存储的 toString 字段现在会被忽略,而不是复制到共享函数上;命名继承属性的点路径会变成无操作。
修复对公告描述的三条代码路径是完整的。应用仍应避免将原始请求对象传入模式期望标量的过滤器;sanitizeFilter 降低风险,但不验证应用语义。
如何检查是否已遭入侵?
公告未公布入侵指标,也未发现实际利用。证据会在应用层,而不是已知的文件或网络特征。
查找代码期望标量的地方却携带操作符对象的查询。如果应用记录查询过滤器,请在请求来源的值中搜索 $ne、$gt、$where 或 $expr:
grep -E '\$(ne|gt|gte|lt|lte|where|expr)' /var/log/your-app.log
命中是线索,不是证据:合法代码也可能使用这些操作符。请将其与同一源地址的认证或基于计数的授权事件关联。
在应用日志中查找继承属性缺陷的拒绝服务症状:
grep -E '_toString\.call is not a function|_toString\.call is not' /var/log/your-app.log
进程在加载或更新文档后开始对每个查询抛出该错误,与该缺陷一致。重启进程会清除症状,但存储的文档仍然存在,因此请找到并清理该记录。
检查集合中是否有字段名遮蔽内置函数的文档。此查询返回线索而非结论,因为合法模式也可能使用这些名称:
db.getCollectionNames().forEach(function(name) {
const hit = db[name].findOne({ $or: [{ toString: { $exists: true } }, { valueOf: { $exists: true } }] });
if (hit) { print(name); }
});
如果发现攻击者写入的文档,请删除它,轮换应用进程可读取的任何凭据,并审查受影响查询返回的记录。
时间线
- 2026-08-09:
countDocuments修复提交。 - 2026-08-10:Mongoose 6.13.11、7.8.12、8.24.3 和 9.9.2 发布
countDocuments修复。 - 2026-10-22:连接和继承属性修复提交。
- 2026-10-23:Mongoose 8.24.7 和 9.11.4 发布第二批修复。
- 2026-11-09:CVE-2026-125120、CVE-2026-125119 和 CVE-2026-125117 的 CVE 记录发布。
- 2026-11-10:GitHub 公告发布。
常见问题
这影响所有 Mongoose 应用吗?
不。严重缺陷要求应用在依赖 sanitizeFilter 的同时使用攻击者控制的过滤器值调用 countDocuments。连接缺陷要求在 createConnection 上设置该选项。继承属性缺陷范围更广:任何使用攻击者控制的字段名加载文档或设置点路径的应用都可能受影响。对三者而言,更新都是安全默认。
有公开的概念验证吗?
连接和继承属性缺陷有。两个公告都包含演示绕过的代码,CISA 对 CVE-2026-125117 的 SSVC 块记录为 Exploitation: poc。countDocuments 公告包含回归测试而非独立利用代码。
是否已在野利用?
截至 2026 年 11 月 11 日未发现实际利用,三个 CVE 均未列入 CISA 的 Known Exploited Vulnerabilities 目录。公开的概念验证代码意味着从披露到尝试的窗口很短,因此请按正常计划更新,不要等待报告。
哪个版本是安全下限?
使用 6.13.12、7.8.13、8.24.7 或 9.11.4。8 月版本只修复严重的 countDocuments 缺陷;10 月版本增加连接和继承属性修复。安装 8.24.3 或 9.9.2 会留下两个 High 缺陷。
为什么修复数月后才发布公告?
修复于 2026 年 8 月和 10 月发布,但 GitHub 公告和 CVE 记录于 2026 年 11 月 9 日和 10 日发布。对于后来才审查的仓库公告,这种间隔很常见。时间线中的日期来自发布标签和 CVE 记录,文章使用修复日期而非公告日期描述补丁历史。
如果我不使用 sanitizeFilter,还需要更新吗?
需要,因为继承属性缺陷不依赖 sanitizeFilter。它依赖攻击者控制的字段名到达 Mongoose 的文档水合、点路径设置器或合并代码。如果模式接受任意键,即使过滤器防御关闭,该缺陷也适用。
使用 Ostorlab 查找易受攻击的 Mongoose 版本
Ostorlab 的软件成分分析和 SBOM可清点仓库和容器镜像中的开源依赖,让你看到哪些服务仍将 mongoose 解析为受影响版本,并跟踪升级到 6.13.12、7.8.13、8.24.7 或 9.11.4。对构建 Node.js 服务的仓库运行该分析,并用结果优先处理将请求数据传入查询过滤器的服务。
参考文献
- Mongoose 公告 GHSA-pcrw-h46h-c93h(CVE-2026-125120):https://github.com/Automattic/mongoose/security/advisories/GHSA-pcrw-h46h-c93h(2026 年 11 月 11 日核查)
- Mongoose 公告 GHSA-gg4r-jv3p-qx3c(CVE-2026-125119):https://github.com/Automattic/mongoose/security/advisories/GHSA-gg4r-jv3p-qx3c(2026 年 11 月 11 日核查)
- Mongoose 公告 GHSA-8332-j7wj-wjw4(CVE-2026-125117):https://github.com/Automattic/mongoose/security/advisories/GHSA-8332-j7wj-wjw4(2026 年 11 月 11 日核查)
countDocuments修复提交(7e912af3):https://github.com/Automattic/mongoose/commit/7e912af3fac52cb0eea4cd71e24d65318916831a(2026 年 11 月 11 日核查)- 连接选项修复提交(4bb65947):https://github.com/Automattic/mongoose/commit/4bb659478be2f30655a7bdd78bc251bdf111cd29(2026 年 11 月 11 日核查)
- 继承属性修复提交(18b5513d):https://github.com/Automattic/mongoose/commit/18b5513dd463731a2caf0d3395ef1d9eb0735f50(2026 年 11 月 11 日核查)
- Mongoose 9.11.4 发布:https://github.com/Automattic/mongoose/releases/tag/9.11.4(2026 年 11 月 11 日核查)
- Mongoose 8.24.7 发布:https://github.com/Automattic/mongoose/releases/tag/8.24.7(2026 年 11 月 11 日核查)
- mongoose 的 npm 下载量:https://npm-stat.com/charts.html?package=mongoose&from=2026-11-03&to=2026-11-09(2026 年 11 月 11 日核查)
- CVE-2026-125120 记录:https://www.cve.org/CVERecord?id=CVE-2026-125120(2026 年 11 月 11 日核查)
- CVE-2026-125117 记录:https://www.cve.org/CVERecord?id=CVE-2026-125117(2026 年 11 月 11 日核查)