立即运行以下命令。如果你的版本号低于下表中的安全基线,说明你的环境受 Node.js Brotli 远程代码执行漏洞(CVE-2026-111497)影响,且攻击者已经在利用该漏洞。
node -v
| 发布分支 | 受影响版本 | 修复起始版本 | 最新版本(2026 年 11 月 5 日) |
|---|---|---|---|
| 26.x | 低于 26.12.1 | 26.12.1 | 26.13.0 |
| 24.x | 低于 24.22.1 | 24.22.1 | 24.22.2 |
| 22.x | 低于 22.23.4 | 22.23.4 | 22.23.4 |
| 25.x、20.x 及更早版本 | 已停服(End-of-Life):未修复且未评估 | 无可用修复 | 迁移至 24 或 26 |
若需第二个验证信号,可以直接查询二进制文件:
node -p "require('node:zlib').constants.BROTLI_DECODER_ERROR_FORMAT_BLOCK_SWITCH"
本次修复新增了该常量。我们在三个受支持分支的 9 个官方 macOS 构建版本上运行了该命令:所有已修复构建均输出 -17,所有旧版本构建均输出 undefined。请优先以 node -v 为准,因为向后移植补丁的 Linux 发行版可能不会同步添加该常量。
不要依赖 process.versions.brotli。它在 24.21.0 与 24.22.1 上均输出 1.2.0,在 22.23.3 与 22.23.4 上均输出 1.1.0。Node.js 仅拣选(cherry-pick)了修复提交,并未修改上报的 Brotli 版本号,因此该字段无法反映是否已打补丁。
AI Coding 交流群
如果你也在用 AI 写代码,或者正在研究 Cursor、Claude Code 这些工具,欢迎加入以下交流群。群里平时会聊一些 AI 编程的实际用法、开发工作流,还有各种新工具和新玩法。
核心要点速览(TL;DR)
| 问题 | 结论 |
|---|---|
| 漏洞本质是什么? | Node.js 内置 Brotli 解码器中的越界写入漏洞,别称 Node2Shell 或 HTTP2Shell |
| 潜在影响 | 进程崩溃、内存信息泄露或远程代码执行(RCE) |
| 严重程度 | Node.js 官方公告定级为 HIGH;NVD 评分为 9.8 CRITICAL(CVSS 3.1) |
| 是否存在在野利用? | 是。CISA KEV 已于 2026 年 10 月 27 日将其列入已知被利用漏洞目录 |
| 是否需要认证登录? | 不需要,依据 NVD 攻击向量及 Akamai 的技术分析 |
| 哪些应用可达? | 会解压不可信 Brotli 数据的应用:Express 5 请求体、fetch() 响应、直接调用 node:zlib |
| 修复方式 | 升级运行时、重新构建容器镜像、重启所有进程 |
| 是否有官方临时方案? | 官方公告未提供临时绕过方案。下文的缓解措施可降低暴露面,但无法完全替代升级 |

ELLIO 报告称,在修复发布五天后的 10 月 26 日,其传感器便捕获到了漏洞利用流量。捕获到的载荷针对 x86-64 架构上的 Node.js 24.22.0。虽然该载荷的最终阶段在 ELLIO 的测试中未能成功执行,但 ELLIO 已在官方原版 24.22.0 构建上独立验证了代码执行。CISA 要求美国联邦机构在 11 月 10 日前完成整改,并将其勒索软件利用状态标为“未知(Unknown)”。Akamai 的遥测数据显示,截至 10 月 26 日,61% 的网络仍在运行受影响版本。
Node2Shell 原理解析:Brotli 漏洞究竟做了什么
Node2Shell 是随 Node.js 运行时分发的 Brotli 解码器中的一个越界写入(out-of-bounds write)漏洞。畸形的压缩数据流会导致解码器向一个 8 字节内部缓冲区的末尾之外写入数据,从而破坏进程内存。其后果可能是进程崩溃、内存泄露,或者在服务器上执行攻击者控制的代码。
根本原因在于错误分类不当。无效的块切换(block switch)被误判为“需要更多输入”,而非“数据已损坏”,导致流式解码器持续向这个小型缓冲区追加字节。
该漏洞存在于 Google Brotli 库的 1.0.3 至 1.2.0 版本中,因此不仅限于 Node.js。Node.js 通过将上游提交 0ed4268a 拣选合入 24.22.1 版本及其同批发布版本修复了内置副本。该漏洞由 OpenAI Codex Security 研究员 Khai Tran 与 Sylvie Mayer 报告。
常规依赖扫描工具无法发现此问题,因为解码器内置于运行时而非 npm 依赖包中。我们在未打补丁的 Node.js 24.15.0 上对 Express 5 测试项目运行 npm install 时,报告的漏洞数量为 0。
哪些调用路径会触发漏洞
存在漏洞的运行时只是一半条件,另一半条件是存在将不可信 Brotli 数据传入解码器的调用路径。我们在 Node.js 24.22.1 上对各条常见路径进行了实测:向服务端发送带有 Content-Encoding: br 的无害非法请求体(纯文本 not-valid-brotli),并向各客户端返回合法的 Brotli 压缩响应。
| 调用路径 | 测试版本 | 是否触发解码器? | 实测表现 |
|---|---|---|---|
Express 5 express.json() |
5.3.0 (body-parser 2.4.0) | 是(默认开启) | 返回 400,“Decompression failed” |
Express 4 express.json() |
4.22.3 (body-parser 1.20.9) | 否 | 返回 415,不支持的编码格式 |
Koa @koa/bodyparser |
6.1.0 | 是(默认开启) | 返回 500,日志打印 BrotliDecoder 错误 |
注册了 @fastify/compress 的 Fastify |
9.3.0 | 是(注册插件后) | 返回 400,FST_CP_ERR_INVALID_CONTENT |
| 原生 Fastify | 5.12.8 | 否 | 返回 400,无效 JSON |
裸 node:http 服务端或 http.get |
24.22.1 | 否 | 原始字节直接透传 |
内置 fetch() |
24.22.1 | 是(始终开启) | 自动解压响应内容 |
| axios、got、node-fetch | 1.21.0、16.2.0、3.3.2 | 是(默认开启) | 自动解压响应内容 |
zlib.brotliDecompress*、createBrotliDecompress |
任意版本 | 是 | 这正是底层解码器 API |

有两项结果值得特别注意。首先,即便请求头显式发送了 Accept-Encoding: identity,内置 fetch() 依然会解压 Brotli 数据,因为它是根据响应头而非请求头来决定是否解压的。这意味着你的应用调用的任何外部服务器都能触达解码器,因此 Webhook 测试器、链接预览器或 URL 导入器全都暴露在风险之下。其次,仅凭返回 400 状态码并不能证明安全:Express 5 与原生 Fastify 都会返回 400,但只有 Express 5 实际运行了解码器。请务必检查具体报错信息。
可以用以下三条命令排查代码库中的可达路径:
# 直接调用解码器
grep -rnE "brotliDecompress|createBrotliDecompress" src/
# 会自动解压请求体的解析中间件
npm ls express body-parser @koa/bodyparser @fastify/compress
# 基础镜像与锁定的版本号
grep -rnE "^FROM .*node" --include='Dockerfile*' .
只要新增一处 fetch() 调用,上述排查结论就会改变,因此“当前不可达”依然意味着必须打补丁。
如何修复 CVE-2026-111497
请直接升级到所属分支的最新构建版本,而不只是最低修复版本。截至 11 月 5 日,以下渠道均已提供修复构建:
| 安装方式 | 执行操作 |
|---|---|
| nvm | nvm install 24 && nvm alias default 24 |
| Homebrew | brew upgrade node(或 node@24、node@22) |
| Docker | FROM node:24.22.2-slim,随后执行 docker build --pull |
| Linux 发行版软件包 | 遵循所属发行版的安全公告,发行版可能会在旧版本号下向后移植补丁 |
| CI 流水线 | 更新 .nvmrc、engines 与 node-version,然后重新构建 |
| Serverless | 在已部署函数中打印 process.version 进行核验,不要默认平台已自动升级 |
接着补上最容易被忽略的两个漏洞点:
- 重启所有进程。 正在运行的进程会将旧版二进制文件保留在内存中,因此在磁盘上完成升级后,必须通过 pm2、systemd 或容器编排器重启进程才能生效。
- 重新构建已交付的制品。 旧镜像中依然包含旧版运行时,而按摘要(digest)锁定的镜像需要进行镜像扫描,因为镜像标签本身不会反映底层变更。
最后,在已部署容器内部、CI Runner 以及开发机上分别运行本文开头的自查命令。如需查看各受支持分支的最新安全构建版本,可运行:
curl -s https://nodejs.org/dist/index.json | jq -r '[.[] | select(.security == true and (.version | test("^v(22|24|26)\\.")))] | group_by(.version | split(".")[0]) | .[] | max_by(.date) | "\(.version) \(.date)"'
如果你仍在运行 Node.js 20 或 25,官方不会为你提供任何修复构建,请尽快制定停服版本迁移计划并升级至受支持分支。
今天无法打补丁时的 Node.js Brotli 临时缓解措施
以下措施只能争取时间,无法替代运行时升级。
- 在边缘层拒绝
br。 在反向代理或负载均衡器上拦截Content-Encoding: br,注意不区分大小写,并匹配类似gzip, br的组合列表。 - 在应用层拒绝
br。 将以下中间件放在所有请求体解析器之前。在我们的 Express 5 测试中,它会直接返回 415:
app.use((req, res, next) => {
const encoding = req.headers['content-encoding'] || '';
if (/\bbr\b/i.test(encoding)) return res.status(415).end();
next();
});
- 关闭自动解压。
express.json({ inflate: false })对相同请求同样返回 415。不过这也会一并拒绝 gzip 请求体,启用前请先确认客户端行为。 - 控制出站响应解压。 axios 支持配置
decompress: false,在我们的测试中它会保留未解码的原始字节。内置fetch()没有标准的关闭开关,因此需要通过白名单和出站流量规则(egress rules)限制其可访问的目标主机。 - 缩减爆炸半径。 隔离 Node.js 服务,并限制核心转储(core dumps)的读取权限——ELLIO 指出进程崩溃可能会在转储文件中暴露内存数据。
密切关注 ELLIO 率先观测到的特征:突发的 Brotli 编码请求体扫描(用于探测哪些主机开启了解码)。如果 JSON API 出现异常的 Content-Encoding: br 流量,或者工作进程崩溃率突然飙升,都值得立即深入排查。
证明升级后 API 行为一致:交给编程智能体执行回归验证
运行时版本跨越可能会改变 TLS、DNS 和 HTTP 行为,而 10 月的安全发布同时涉及了这三块。无需手动比对差异,可以让编程智能体在升级前后运行 Apifox CLI 并输出对比报告。
npm install -g apifox-cli@latest
apifox auth login --with-token <YOUR_TOKEN>
apifox skill install # 按提示选择你的编程智能体
向你的智能体发送一段提示词:
在预发布环境(staging)运行项目 <id> 中的 Apifox 测试套件 <id>,并将结果保存到
reports/before。在我完成 Node.js 升级后,为 POST /orders 新增一条测试用例:发送携带
Content-Encoding: br 的纯文本 "not-valid-brotli",断言返回 4xx 状态码,随后检查
GET /health 返回 200。重新运行测试套件并保存到 reports/after,列出所有结果发生变化的请求。
智能体会依次执行以下命令:
apifox run --test-suite <id> --project <id> -e <envId> -r cli,junit --out-dir reports/before
apifox cli-schema get test-case-create
apifox cli-schema validate test-case-create --file case.json
apifox test-case create --project <id> --file case.json
apifox run --test-case <caseId> --project <id> -e <envId> -r cli,json
apifox run --test-suite <id> --project <id> -e <envId> -r cli,junit --out-dir reports/after
新增的那条用例属于健康检查而非漏洞利用载荷:请求体只是普通纯文本,在我们的测试中,它在受影响版本与修复版本上都会触发相同的填充错误(padding error)。但它能捕捉到真实的异常处理缺口——我们的 Koa 测试因为未捕获解码错误而返回了 500,而公开 API 应当返回 4xx。请仅针对预发布环境运行该检查。
你可以通过 apifox run --help 查看所有命令行参数,并结合 CI/CD 流水线指南 将相同的 JUnit 测试集成到流水线中。
为什么接下来的 11 月 11 日同样关键
Node.js 项目已预先预告了下一轮安全发布,将于 2026 年 11 月 11 日当天或之后不久面向 26.x、24.x 与 22.x 推出,最高严重程度同样为 HIGH。目前具体 CVE 细节尚未公开,建议提前盘点运行时资产并准备好补丁日升级流程。
请立即修复 Brotli 漏洞,不要等到 11 月 11 日的窗口才一并处理。该漏洞已有公开细节且存在实时在野攻击,而本周执行的升级正好可以作为下一轮发布的实战演练:同样的资产清单、同样的镜像重建流程、同样的回归测试套件。此前的 PHP2Shell 与 React2Shell 漏洞也呈现出相同的演进规律。
常见问题(FAQ)
CVE-2026-111497 是否正在被在野利用? 是的。CISA 已于 2026 年 10 月 27 日将其归入 Google Brotli 类别并列入已知被利用漏洞(KEV)目录,ELLIO 则在 10 月 26 日前就记录到了在野利用流量。
它到底属于高危(High)还是严重(Critical)? 两个评级都真实有效。Node.js 官方将其定级为 HIGH;NVD 在 CVSS 3.1 下的评分为 9.8 CRITICAL,而 Google 给出的 CVSS 4.0 评分为 9.2。请直接按照“已被在野利用”的最高优先级来制定修复计划。
我正在使用 Express 4,是否安全? 你的入站请求体不会触达该解码器,但出站 fetch()、axios 以及直接调用 node:zlib 依然会触发。此外,同一批安全发布还修复了另外 9 个 CVE,因此无论如何都应尽快升级。
Node.js 20 是否受影响? 官方不会对已停服(End-of-Life)分支进行评估,也不会发布修复补丁。请将 20.x 与 25.x 视为“未修复且未评估”,并尽快完成大版本迁移。
除了 Node.js 之外还有其他组件受影响吗? 有。GitHub 安全公告涵盖了 PyPI 上的 brotli 与 brotlicffi 包,各家厂商也陆续发布了针对 Bun 1.4.4 和 .NET 的修复。由于升级 Node.js 只能更新其自带的解码器副本,请务必分别更新各个运行时与依赖包中的解码器。