Brotli 解码器里一个布尔返回值,把 Node.js、Python 和各路 HTTP 服务拖上了同一个补丁期限
DecodeBlockTypeAndLength() 返回的布尔值分不清「数据坏了」和「还要更多输入」,一段构造过的流就能写出缓冲区边界。上游二月就修了,但没人当成安全漏洞,直到这个月各运行时一起发版。
中文
复制

TL;DR
Brotli 是 Google 开发的一款广泛使用的压缩库。10 月 21 日星期三,NodeJS 披露了 CVE-2026-111497,代号 “node2shell”。解压攻击者可控 Brotli 数据的 Node.js 进程,可能被诱导泄露内存或执行任意代码。星期四,Python 披露其软件包同样受 CVE-2026-111497 影响。
到 10 月 23 日星期五,已经清楚这个 CVE 还影响其他主要语言以及使用 HTTP 的服务应用,相关补丁尚未完全就绪。
请检查你的库是否将 Brotli 作为依赖进行解码。留意后续更广泛的公告,并做好尽快升级的准备。如果可能受影响的库或应用尚无可用补丁,我们建议在补丁发布前禁用 Brotli 解码。
太平洋时间下午 4:12 更新。补丁已发布,涵盖 libbrotli、Python、C++、NodeJS、bun、Envoy,更多补丁还在陆续推出。Apache 和 Nginx 默认不解码 brotli。
太平洋时间下午 4:15 更新。OpenAI 表示他们早在 2 月就发现了该漏洞。当时 Brotli 源码中的根因问题已经修复,但并不知道它会引发安全漏洞。OpenAI 的公告见此处。
什么是 Brotli?
Brotli 是 Google 开发的一种无损压缩算法和数据格式,用于缩小数据以便存储或通过互联网传输,软件再将其解包还原出原始数据。它广泛用于 HTTP 压缩,用来压缩 HTML、CSS 和 JavaScript 等网页内容。
什么是 CVE-2026-111497?
CVE-2026-111497 是 Google Brotli 解码器中的一个越界写入漏洞,影响所有平台上 1.0.3 至 1.2.0 的版本。这与几天前公布的 Node2Shell 背后的 Brotli 解码器缺陷是同一个。远程未认证的攻击者可以发送精心构造的 Brotli 压缩流,从而破坏内存、泄露敏感数据、使进程崩溃,甚至可能执行任意代码。其 CVSS v4.0 评分为 9.2(严重),Node.js 因其内置的 Brotli 库而受影响。
这个缺陷让解码器在本该拒绝数据时继续接收更多字节。这些字节会溢出一个小的工作缓冲区,破坏程序内存。攻击者可以利用这种破坏来控制运行什么代码,比如植入自己的代码。
OpenAI 在二月份发现了这个问题,并将其报告为无需认证的远程代码执行漏洞,且双向均可触发:恶意客户端可以接管未打补丁的服务器,恶意服务器也可以接管接受压缩响应的未打补丁的客户端。
根因已于二月份在 Brotli 源码中修复,但当时并未被认定为安全问题,直到现在才有包含该修复的版本发布。这意味着每个捆绑了 Brotli 的项目都必须自行发布更新。
只要任何环境中安装了 Brotli 1.2.0 或更早版本,或 brotlicffi 1.2.0.x,你就处于风险之中,因为 requests、httpx、aiohttp 等 HTTP 客户端在检测到这些包时会自动加载它们。请升级到 Brotli 1.2.1 或 brotlicffi 1.2.1.0,并重新构建所有捆绑了它们的容器镜像。截至 10 月 23 日,漏洞公告数据库尚未将 CVE 映射到这两个 PyPI 包,因此 pip-audit、Dependabot 和 Safety 暂时不会标记存在漏洞的版本。
你可以在 Aikido Intel 中查看我们追踪的相关信息。
至于不受影响的软件,Apache 标准的 mod_brotli 不解码 Brotli。
如何修补 HTTP2Shell CVE-2026-111497
针对 CVE-2026-111497,把受影响的运行时或 Brotli 库更新一下。
- Node.js: 升级到 22.23.4、24.22.1 或 26.12.1,或对应发布线中更新的受支持版本。
- 原生 Brotli / libbrotli: 升级到 1.2.1 或更高版本。对于较旧的发布线,使用已修补的 1.0.10 或 1.1.1。
- Python 包: 将所有已安装的 Brotli 升级到 1.2.1 或更高版本,brotlicffi 升级到 1.2.1.0 或更高版本。
- curl / libcurl: 将其 Brotli 依赖更新到 1.2.1、已修补的旧版本,或厂商提供的安全修复包。捆绑或静态链接的副本需要重新构建应用程序。
- Bun: 升级到 1.4.4 或更高版本。重新编译捆绑了 Bun 的可执行文件。
- Envoy: 升级到 1.40.0、1.39.3、1.38.6 或 1.37.8,或受支持分支中更新的版本。1.36.12 也包含该修复,但 1.36 分支已不再受支持。
- Istio: 该修复已包含在 1.29.9、1.30.6 和 1.31.2 中。请使用对应发布线中更新的受支持版本;1.29 分支已停止维护。
- 操作系统包: 安装厂商的安全更新。厂商可能在不更改上游版本号的情况下回移该修复。
- 其他 HTTP 服务器、反向代理、API 网关和 HTTP 客户端: 更新其使用的受影响运行时或 Brotli 库。如果 Brotli 捆绑在产品中,请安装明确针对此 CVE 的产品厂商版本。对于托管服务,请向提供商确认修复情况。
更新依赖锁文件,重新构建受影响的应用程序和容器镜像,重新部署并重启服务。确认正在运行的工作负载使用的是已修补的组件。
| 项目 | 补丁状态 | 修复版本 | 备注 |
|---|---|---|---|
| Node.js | 已有补丁 | 22.23.4 · 24.22.1 · 26.12.1 | 请使用仍受支持的发布线。 |
| Bun | 已有补丁 | 1.4.4 | 包含 Brotli 1.2.1。 |
| Envoy | 已有补丁 | 1.40.0 · 1.39.3 · 1.38.6 · 1.37.8 | 1.36.12 也包含修复,但 1.36 已不受支持。请使用受支持的分支。 |
| Istio | 已有补丁 | 1.29.9 · 1.30.6 · 1.31.2 | 1.29 已停止维护,请迁移到受支持的版本线。 |
| Google Brotli(上游) | 已有补丁 | 1.0.10 · 1.1.1 · 1.2.1 | 根据你所用库的分支选择对应的修复版本。 |
暂时无法升级时的应对措施
升级准备好之前,下面这些临时方案能降低暴露风险。
- Python 应用: 如果 Brotli 支持是可选的,请从受影响的环境中卸载 Brotli 和 brotlicffi。移除
httpx[brotli]之类的依赖 extras,以及任何会重新安装它们的其他依赖项,然后重新生成依赖锁文件,并重新构建、重启工作负载。缺少这些可选包时,HTTPX、Requests 和 urllib3 仍保留对 gzip 和 deflate 的支持。不过,如果服务端仍然发送 Brotli,可能产生无法解析的响应或应用报错;不保证会自动回退。- 卸载这两个 Python 包并重启工作负载,会移除这些包为收发数据提供的解码路径,但不会移除其他运行时、库或产品中内置的独立 Brotli 解码器。
- 入站 HTTP 请求: 在反向代理、负载均衡器或应用中间件处,于任何存在漏洞的解码器处理请求体之前,拒绝
Content-Encoding中包含br的请求。匹配br时不区分大小写,包括出现在编码列表中的情况。 - 出站 HTTP 请求: 从
Accept-Encoding中移除br。同时禁用 Brotli 响应解码,或在解码开始前拒绝Content-Encoding中包含br的响应。务必确保该检查发生在自动处理响应体之前;等客户端下载并解码完响应再检查就太晚了。
只改 Accept-Encoding 还不够:恶意服务器可以无视客户端声明的内容,照样返回 Brotli 响应,而 urllib3 是根据响应里的 Content-Encoding 头来选择解码器的。
在受影响组件完成修补并验证之前,请保持这些控制措施。厂商确认更多受影响组件或攻击路径后,请复查覆盖范围。
Aikido 如何应对 CVE-2026-111497
Aikido 会在你的代码仓库、容器镜像和依赖中标记出存在漏洞的 Brotli 与 brotlicffi 版本,包括以传递依赖形式引入的情况。仪表盘会显示哪些服务仍在运行有漏洞的版本,你可以优先升级那些会调用外部服务器或接受压缩请求体的服务。
升级之后,Aikido 会检查该问题是否已经消失。
时间线
Brotli 是 Google 开发的一款广泛使用的压缩库。10 月 21 日星期三,NodeJS 披露了 CVE-2026-111497,代号 “node2shell”。解压攻击者可控 Brotli 数据的 Node.js 进程,可能被诱导泄露内存或执行任意代码。只要你的应用或某个库会解压恶意的 Brotli 数据(例如内置的 fetch()、body-parser),就会受到影响。
第二天,Python 发布了安全更新,修补了同一个缺陷;Python 包同样受 CVE-2026-111497 影响。鉴于该库在下游被广泛集成,这一 Brotli 缺陷可能波及更多语言和服务应用。原来 OpenAI 早在二月份就发现了这个问题。
CVE-2026-111497 的技术细节
缺陷位于 DecodeBlockTypeAndLength(),该函数负责处理块切换,也就是 Brotli 流更换所用 Huffman 树的位置。在有漏洞的版本中,这个函数只返回一个普通布尔值,而 BROTLI_FALSE 既可能表示“这条流格式有误”,也可能表示“我还需要更多输入”。调用方无法区分这两种情况,而在快速路径上,解码器干脆丢弃了返回值。
构造出来的流可以先声明单一块类型,然后耗尽该块的长度,从而强制触发一次无法成功的切换。此时解码器要么对一条已经完整的流报告需要更多输入,要么沿用已有的块状态继续解码。后一条路径会导致越界写入。
上游修复 google/brotli commit 0ed4268 让该函数返回错误码,并新增了一个错误 BROTLI_DECODER_ERROR_FORMAT_BLOCK_SWITCH(-17)。现在无论走安全路径还是快速路径,解码都会在遇到格式错误时停止。OSM 对比了 Brotli 1.2.0 和 1.2.1 的源码发行包,发现 1.2.1 与 1.2.0 的差别只有版本号变更和该 commit 带来的解码器改动。
Python 库中的 Brotli 漏洞(CVE-2026-111497)
Node2Shell 背后的 Brotli 漏洞(CVE-2026-111497)同样影响 Python。PyPI 上的两个包 Brotli 和 brotlicffi 编译的是与 Node.js 捆绑的同一份存在漏洞的 Brotli 解码器。GitHub 上已发布官方公告。Brotli 每月下载量达 8280 万次。
只要任一 Python 环境安装了 Brotli 1.2.0 或更早版本,或 brotlicffi 1.2.0.x,就会受影响。requests、httpx 等常用 HTTP 客户端在这些包存在时会自动加载它们。有些包会把 Brotli 作为依赖自动安装,比如 Scrapy;另一些则需要显式指定。攻击者可以对你应用发出的某个请求返回一个恶意的 Brotli 响应,也可以向你应用发送一个用 Brotli 压缩的请求体,两条路都能触达存在漏洞的解码器。也就是说,即使应用代码里从未 import Brotli,也可能暴露在风险中。它是常见的传递依赖,请检查你的系统。
修复就是升级到打了补丁的版本。
- Brotli → 1.2.1
- brotlicffi → 1.2.1.0
两个修复版本的发布说明都没有提到这个 CVE。截至 10 月 23 日,漏洞公告数据库尚未把它关联到这两个 PyPI 包,因此 pip-audit、Dependabot 和 Safety 都不会标记这些版本。
哪些 Python 用户受 CVE-2026-111497 影响?
只要任一 Python 环境装了下面任意一个包,就会受影响。
| 包 | 受影响版本 | 修复版本 |
|---|---|---|
| Brotli(PyPI) | 1.2.0 及更早 | 1.2.1 |
| brotlicffi(PyPI) | 1.2.0.x | 1.2.1.0 |
你的代码不必 import 这两个包中的任何一个。下面这些 HTTP 客户端会声明 Accept-Encoding: br 并自动解码 Brotli 响应。
- 安装了 Brotli 或 brotlicffi 时的 urllib3(以及依赖它的 requests)
- 带 brotli extra 的 httpx
- 安装了 Brotli 时的 aiohttp
- Scrapy,以及任何基于上述客户端构建的工具
攻击者的数据可以走两条路进入解码器。
- 出站请求。你的应用从攻击者控制的服务器获取 Brotli 响应,比如通过 webhook 投递、URL 预览、爬虫或“抓取这个 URL”之类的功能。
- 入站请求。你的框架或中间件会解码用
Content-Encoding: br发送的请求体,这相当于 Node.js 中 Express 5 的那个案例在 Python 里的版本。
如何在 Python 中修复 CVE-2026-111497
修复就是升级软件包,一共四步。
- 用
pip install -U "Brotli>=1.2.1"或pip install -U "brotlicffi>=1.2.1.0"升级你已安装的那个包。如果你用 Poetry、uv 或 pip-tools 管理锁文件,先更新版本约束,再重新生成锁文件。 - 重新构建容器镜像,以及任何打包了这两个包的 wheel。基于有漏洞版本构建的镜像,在你重新构建之前一直是有漏洞的。
- 检查你运行的每一个环境,包括笔记本、CI runner、服务器和容器。很多项目是通过 HTTP 客户端的 extra 引入 Brotli 的,所以要查看完整的依赖解析树。
- 目前只能靠手动检查。在公告数据库把该 CVE 映射到 PyPI 包之前,pip-audit、Dependabot 和 Safety 都不会报告这个问题。
如果你的 Python Brotli 绑定来自 Linux 发行版的软件包而非 PyPI,那么修复要经由你的发行版,因此也要查看该发行版的安全公告。
Python Brotli 漏洞(CVE-2026-111497)是什么?
Python Brotli 漏洞指的是 CVE-2026-111497,即被称为 Node2Shell 的 Brotli 解码器缺陷在 PyPI 上 Python 包中的表现。该 CVE 记录将其归类为 CWE-252(未检查返回值)与 CWE-787(越界写入)。
CVE-2026-111497 对 Python 有多严重,是否已被利用?
CVE 记录给该缺陷的 CVSS 4.0 评分为 9.2,Node.js 将其定为高危。OSM 用 AddressSanitizer 构建了 Brotli 1.2.0 和 1.2.1,并让两者分别处理同一个构造的流。无论流是一次性送达,还是被切成小到 1 字节的分块,1.2.0 都读完了整个约 23 MB 的流,并一直报告需要更多输入。1.2.1 则每次都返回错误 -17 拒绝该流。
OSM 并未在 Python 扩展中尝试内存破坏或代码执行。研究人员用来攻击未修改的 Node.js 24.22.0 构建的,正是这段解码器代码,Imperva 等机构已有报道。就 Node.js 而言,截至 10 月 22 日,The Hacker News 报道称尚未发现野外利用。
不过还是要把它当成紧急事件处理。这是处理不可信网络输入的组件中的内存安全缺陷,修复方式只是例行升级版本。调用外部服务器的服务,以及接受压缩请求体的服务,应优先处理。
如何检查 Python 应用是否受影响
- 在每个环境中用
pip show Brotli brotlicffi检查已安装版本,对每个容器镜像用docker run --rm <image> pip show Brotli brotlicffi检查。 - 查明这些包是由谁引入的。运行
pipdeptree -r -p Brotli查看哪些依赖需要它,并用grep -i brotli poetry.lock uv.lock requirements*.txt搜索 lockfile。 - 列出指向你无法控制的服务器的出站 HTTP 调用,以及所有会解码
Content-Encoding: br请求体的端点。 - 先升级符合第 3 步的服务,再处理其余服务。
如果你在 Python 中复现该漏洞,brotli.decompress() 对任何错误都抛出通用的 brotli.error: decoder failed,因此看不到具体是哪个错误码触发。用 Decompressor() 增量喂入构造的流,在 1.2.0 上永远不会结束。
FAQ
哪些版本修复了 Python 中的 CVE-2026-111497?
Brotli 1.2.1 和 brotlicffi 1.2.1.0,以及任何更新的版本。
我需要在代码中 import Brotli 才会受影响吗?
只要安装了包就够了。urllib3、requests、httpx 和 aiohttp 在检测到 Brotli 或 brotlicffi 时会自动使用它们,并解码你的应用所调用的任何服务器返回的 Brotli 响应。
pip-audit 或 Dependabot 会标记这个问题吗?
目前不会。截至 10 月 23 日,CVE 记录、OSV 和 GitHub Advisory Database 都没有列出这两个 PyPI 包。OSM 正在提交 PYSEC 条目,并为 GitHub advisory 提交 PyPI 包映射,以便工具能够识别。
有临时规避方法吗?
卸载 Brotli 和 brotlicffi 即可移除存在漏洞的解码器,HTTP 客户端会回退到 gzip 和 deflate。在边缘拒绝 Brotli 编码的请求可以减少入站暴露面,但无法防护你的应用主动获取的 Brotli 响应。