经验笔记
扫描器还没亮红:手机上把 Brotli 传递依赖查清并升包重建
CVE-2026-111497(Node2Shell→HTTP2Shell)同一套 Brotli 解码器进了 PyPI 的 Brotli/brotlicffi;requests、httpx 等装上后可能自动解 br。截至 10-23,Dependabot/pip-audit 等仍可能未映射 PyPI——扫描全绿≠安全。手机 SSH 上查 node/pip/锁文件,升到补丁线后重建镜像并重启。

昨天很多人在忙 PHP 补丁:确认版本、升包、重启 worker。今天这篇不是那一套的换皮——主线换成 Brotli 传递依赖,以及 扫描器还没把 PyPI 映射进告警。
据 CVE-2026-111497(CVSS v4.0 9.2):Google Brotli 解码器在 1.0.3–1.2.0 存在越界写;恶意压缩流可能导致崩溃、内存泄漏或远程代码执行。上游修复线为 1.0.10 / 1.1.1 / 1.2.1。Node.js 官方安全通告(2026-10-21 Security Releases,社区常称 Node2Shell)已给出运行时补丁:22.23.4 / 24.22.1 / 26.12.1。
随后 Aikido:Node2Shell evolves to HTTP2Shell(Published Oct 23, Last updated Oct 24)指出:同一套解码器还进了 PyPI 的 Brotli / brotlicffi。requests、httpx、aiohttp 等在环境里装了这些包时,可能自动处理 Content-Encoding: br——你自己的代码未必 import brotli,传递依赖照样把解码路径打开。HN 讨论见 item?id=50216791。
Aikido 写明:截至 2026-10-23,advisory 数据库仍可能尚未把该 CVE 映射到上述两个 PyPI 包,因此 pip-audit、Dependabot、Safety 暂时不会红灯。扫描全绿,只说明工具还没看见——不等于机器上已经安全。
先把「谁在用 Brotli」揪出来
补丁版本(一手核对,以官方/发行说明为准):
| 组件 | 修复线 | 一手源 |
|---|---|---|
| Node.js | 22.23.4 · 24.22.1 · 26.12.1 | nodejs.org 安全通告 |
| Bun | ≥ 1.4.4 | Aikido 补丁表 |
| Google Brotli(上游) | 1.0.10 · 1.1.1 · 1.2.1 | CVE 记录 |
| PyPI Brotli | ≥ 1.2.1 | pypi.org/project/Brotli/1.2.1 |
| PyPI brotlicffi | ≥ 1.2.1.0(changelog 写明 remediation CVE-2026-111497) | pypi.org/project/brotlicffi/1.2.1.0 |
下面按「查传递依赖 → 升包/改锁 → 重建镜像 → 重启并复核」写。命令是常见环境示意;包管理器、服务名、编排入口以你机器为准。
# 1) Node 侧:运行时是否已到补丁线
node -v
# 修复线:v22.23.4 / v24.22.1 / v26.12.1(或更新的受支持发行)
# Bun(若用):bun -v # ≥ 1.4.4
# 2) Python 侧:Brotli 是否作为直接或传递依赖装上了
pip show Brotli brotlicffi 2>/dev/null || true
# 容器镜像里再查一遍(把 IMAGE 换成实际镜像)
# docker run --rm IMAGE pip show Brotli brotlicffi
# 谁把它拉进来的(可选)
pipdeptree -r -p Brotli 2>/dev/null || true
pipdeptree -r -p brotlicffi 2>/dev/null || true
# 3) 锁文件 / 依赖树里搜 brotli(传递依赖常藏在这里)
grep -i brotli poetry.lock uv.lock requirements*.txt Pipfile.lock package-lock.json yarn.lock pnpm-lock.yaml 2>/dev/null || true
# 4) 升到补丁线(按你实际用到的栈;升完必须重建并重启)
# Node(示例:用官方安装脚本/包管理器升到对应 LTS 补丁版,以你发行源为准)
# Python:
pip install -U 'Brotli>=1.2.1' 'brotlicffi>=1.2.1.0'
# Poetry / uv / pip-tools:改约束后重新生成锁文件,再安装
# 5) 重建镜像 / 重装依赖,再重启跑请求的那个进程
# 例:docker build ... && docker compose up -d --force-recreate
# 或:systemctl restart <your-app>
# 6) 复核:运行中的环境版本,而不是只看构建机
node -v
pip show Brotli brotlicffi
要点只有一句:锁文件改了、镜像没重建,或包装上了、进程没换,都还是旧解码器。 多机、CI runner、笔记本本地 venv 也要逐环境查——Aikido 提醒很多项目是通过 HTTP 客户端的 extra 拉进 Brotli 的。
赶不上升级时,官方/厂商侧常见权宜包括:可选场景下卸掉 Brotli/brotlicffi(并去掉会重装它们的 extra)、在边缘拒绝 Content-Encoding: br、出站去掉 Accept-Encoding 里的 br 且在解码前拒绝 br 响应。这些是缓冲,不是终点;Aikido 也写明只改 Accept-Encoding 不够——恶意服务端仍可能直接回 br。

扫描全绿时,人往往不在工位
Dependabot / pip-audit 还没亮红,并不等于你可以等到下周例行升级窗口。刷到 Aikido / HN 时,人多半在通勤、饭局或出差——开电脑、翻密码本、对一遍锁文件,窗口已经空转过去。
我给自己留的入口是微信搜 OrcaTerm:小程序即开即用,主机列表和登录配置与 PC / Web 云端同步,不用临时翻密码本。连上之后,用说人话让 AI 把「查传递依赖 → 升包 → 重建 → 重启 → 复核」写成命令并在终端回显,例如:
- 这台机
node -v有没有到 22.23.4 / 24.22.1 / 26.12.1 pip show Brotli brotlicffi现在是多少;锁文件里有没有 brotli- 按当前包管理器写出升级与重建步骤,升完再核一遍版本
需要把 poetry.lock、构建日志或 pip show 输出丢回值班群时,可以在小程序里把文件取下来再经微信转出去。
边界说清楚:OrcaTerm 缩短的是「人不在电脑前也能查清传递依赖并完成升包重建」的空窗;它不是 WAF,也不能替你挡住有缺陷的解码器,更不能代替官方补丁与镜像重建。CVE 记录、Node 安全通告与 Aikido 补丁表才是处置口径的一手源。

收束:先查依赖树,再信扫描结果
可以收成三句:
- 栈不同——昨天是 PHP 运行时确认+升包+重启;今天是 Brotli 传递依赖(Node 捆绑 + PyPI
Brotli/brotlicffi),扫描器可能尚未映射。 - 全绿≠已修——以
node -v、pip show、锁文件与跑请求的那个镜像/进程为准;升完重建并重启后再核一次。 - 入口要在口袋里——热点刷到时人往往不在工位;手机能开的 SSH,比「等回家开电脑」短一个数量级。
补丁版本表与 workaround 细节见:Aikido HTTP2Shell 博文 · Node.js 2026-10-21 安全通告 · CVE-2026-111497
微信搜「OrcaTerm」,官网:https://orcaterm.com/