安全扫描的工单挂到还在跑 Node 18 的服务上,编号 CVE-2026-111497,运维那边回得也很干脆:依赖太老,这个季度排不进升级窗口。可工单不会因为这句话自己关掉,至少得搞清楚这个洞打在哪,升级之前手上能做点什么。
这个编号是 Node 官方 10 月 21 日那批安全更新里的一条,问题出在 Node 自带的 Brotli 解码器。遇到结构不合法的压缩流,解码器没有直接报格式错误,而是当成输入还不够,继续等,结果一直往一个 8 字节的内部缓冲区里追加数据,写出了边界。官方写的后果是进程崩溃、泄露进程内存,或者远程代码执行,级别标的是 High。有些第三方通告按 critical 定级,给的分数也不统一,内部评级建议直接引 Node 官方公告:
官方这次只给 22、24、26 三条线出了补丁:
22.x -> 22.23.4
24.x -> 24.22.1 (注意是 24.22.1,停在 24.22.0 不算)
26.x -> 26.12.1
18 不在里面。官方的版本列表里 18、20、23、25 都已经是 EOL,公告也写了,每次安全发布时停止维护的版本一律视为受影响,所以别等 18 的补丁,不会有。版本列表:https://nodejs.org/en/about/previous-releases
线上跑的是哪个版本,在进程所在的环境里看,容器就进容器看,别拿开发机的输出糊弄工单:
node -v
node -p "process.versions.node + ' / brotli ' + process.versions.brotli"
如果 node 是从发行版源装的,打包方可能自己回移补丁,这种情况要看发行版的安全公告,不能只看主版本号。
哪些地方会碰到
触发条件是应用去解压攻击者能控制的 Brotli 数据。光一个裸的 http.createServer 不会自己解请求体,要排查的是下面这些位置:
代码里直接对外部输入调 zlib.brotliDecompress() / zlib.createBrotliDecompress() 的,比如处理上传的压缩包、解第三方推过来的数据。
内置 fetch() 收到 Content-Encoding: br 的响应会自动解压。Akamai 的分析把这点单独拎了出来:只要你请求的上游是恶意的或者被攻破了,客户端这边就会被打。
用 Express 5 的也在范围里。Express 5 依赖 body-parser 2,body-parser 的 HISTORY 写着 brotli 支持是 2.0.0 加进来的,README 里 json / raw / text / urlencoded 几个解析器都会自动解 br,inflate 默认是 true。所以任何客户端发一个带 Content-Encoding: br 的请求体,默认就会被解压。Express 4 配的是 body-parser 1.x,按 HISTORY 看那条线没有 brotli,不过项目里如果自己装了 body-parser 2,一样要查。
文档:GitHub - expressjs/body-parser: Node.js body parsing middleware · GitHub
升级前先挡一挡
入口能控制的话,在网关直接拒掉带 br 编码的请求体,一般的业务接口很少需要收压缩过的请求体:
# server 或 location 里
if ($http_content_encoding ~* "br") {
return 415;
}
应用层再补一道,挂在所有 body 解析中间件前面。确定客户端不传压缩体的,干脆把 inflate 关掉,压缩请求体会直接被拒成 415。代价是 gzip 的请求体也一起被拒,有老客户端在传 gzip 的话先确认一下:
app.use((req, res, next) => {
const enc = String(req.headers['content-encoding'] || '').toLowerCase();
if (enc.includes('br')) return res.status(415).end();
next();
});
app.use(express.json({ inflate: false }));
app.use(express.urlencoded({ extended: false, inflate: false }));
出站这边,fetch() 能访问的目标收成白名单,再配合出口防火墙或安全组限制这台机器能连哪些地址。只改请求头里的 Accept-Encoding 没多大用,恶意服务器根本不会理你要什么编码。自己直接调 zlib 解 Brotli 的代码,能下线就下线,至少别再处理来源不明的数据。
这些都只是缓解,把已知的入口堵住,不等于漏洞没了。官方公告里没有给任何 workaround,要修掉只能迁到还在维护的版本线,并且至少是上面那三个补丁版本。18 直接跳 22 或 24 肯定有兼容性要处理,工单里可以先把网关和 inflate 的改动写上,同时把迁移排期定下来,别让这季度不升拖成一直不升。实在短期迁不动的,官方版本页上提到 EOL 版本可以通过 OpenJS 生态可持续计划的合作方买商业支持,这个就看公司愿不愿意掏钱了。
其他参考:
- 22.23.4 发布说明:Node.js — Node.js 22.23.4 (LTS)
- 24.22.1 发布说明:Node.js — Node.js 24.22.1 (LTS)
- 26.12.1 发布说明:Node.js — Node.js 26.12.1 (Current)
- 新加坡 CSA 通告:https://www.csa.gov.sg/alerts-and-advisories/alerts/al-2026-154/
- Akamai 技术分析:https://www.akamai.com/blog/security-research/inside-node2shell-closer-look-cve-2026-111497
还卡在 18 上的,你们网关那层是怎么拦的?有没有遇到客户端真在传 br 请求体的情况?