Google 的 Brotli 解壓縮函式庫在 1.0.3 至 1.2.0 版本存在越界寫入漏洞,編號 CVE-2026-111497,評分 9.2。Node.js 22、24 與 26 已推出修補版,Python 的 Brotli 亦更新至 1.2.1,會解壓他人資料的服務應立即檢查。
Node.js 的修補版本為 22.23.4、24.22.1 與 26.12.1,隨十月二十一日的月度安全更新一併推出。任何會解壓他人提供資料的服務,都應立即核對版本、重新啟動程序或重建映像,因為磁碟上的套件更新不會替換已載入記憶體的解碼器。

Brotli 解壓漏洞是什麼?
Brotli 是 Google 開發的壓縮格式,用於傳輸與字型檔。其 C 解碼器有越界寫入缺陷,特製壓縮流可覆寫相鄰記憶體並執行任意程式碼。
這個漏洞由 OpenAI Codex Security 的 Khai Tran 與 Sylvie Mayer 向 Node.js 專案回報,Node.js 團隊的 Matteo Collina 與 Antoine du Hamel 完成修補。Google 同日發佈 Brotli 1.0.10、1.1.1 與 1.2.1 三個修補版本,NVD 在十月二十二日公開記錄,GitHub 亦發佈安全公告 GHSA-9v8h-qqgm-4m35。
評分差異值得留意。GitHub 依 CVSS 4.0 給出 9.2 分,屬嚴重級別;Node.js 專案沿用 CVSS 3.1 的判斷,標為高危。兩者都指向同一個結論:這個缺陷可以被遠端、未經認證的攻擊者觸發,而且不需要任何使用者互動。
漏洞名稱的來源有一段插曲。這個缺陷最初被稱為 Node2Shell,但實際問題出在 Brotli 本身,影響範圍遠超 Node.js。安全研究員 Juliano Rizzo 因此改名為 Broccoli,理由是壓縮函式庫的缺陷影響面更接近二十年前的 zlib,而 zlib 當年的修補拖了很久。
哪些版本受影響,應該升級到哪一版?
受影響版本應升至 1.0.10、1.1.1 或 1.2.1。Node.js 三線升至 22.23.4、24.22.1 或 26.12.1,Python 兩套件升至 1.2.1。
Node.js 方面,修補版本在十月二十一日隨月度安全更新一併推出。三條仍在維護的發佈線分別對應 22.23.4、24.22.1 與 26.12.1,較早的 25、23、20 及更早版本全部列入生命週期終止,官方沒有提供修補,唯一做法是升級至 22、24 或 26。
Python 方面,PyPI 上的 Brotli 與 brotlicffi 都編譯同一份 C 解碼器。Brotli 1.2.1 在十月二十二日上架,brotlicffi 1.2.1.0 於次日跟進。若套件由 Linux 發行版提供,例如 python3-brotli,修補則隨發行版的安全更新派送。
系統層的 libbrotlidec 亦在範圍之內。Debian 與 Ubuntu 已為 bookworm、trixie 等版本推送帶安全修補的套件,版本號後綴帶有安全更新標記。動態連結此函式庫的程式在更新套件並重新啟動後即受保護;靜態連結或自帶副本的程式,需要自行重新編譯。
為什麼一般應用也會中招?
多數客戶端會自動解壓 Brotli 回應,Node.js 的 fetch 與 Python 的 requests 都會解碼,任何可回應請求的伺服器都能送出惡意壓縮流。
第一個入口是對外請求。Node.js 內建的 fetch 會自動解碼標記為 br 的回應,Python 的 requests、urllib3 與 aiohttp 在偵測到 Brotli 套件時同樣如此。這意味著網頁鉤子、連結預覽、爬蟲與「從網址匯入」功能,都可能成為觸發路徑。
第二個入口是入站請求內容。Express 5.0.0 至 5.3.0 與 body-parser 2.0.0 至 2.3.0 會在 raw 與 json 解析器中解壓 Brotli 請求內容。需要強調的是,這兩個套件本身沒有缺陷,升級它們無法解決問題,改動必須發生在執行環境或解碼函式庫。
第三個入口是直接呼叫解碼器。zlib.brotliDecompress、createBrotliDecompress、brotli.decompress,以及任何處理壓縮檔、快取或佇列訊息的程式碼,都在暴露面上。Node.js 自帶 Brotli 副本,因此更新系統的 libbrotli 對它沒有作用,只有 Node.js 版本本身有效。
掃描工具在這一次並不可靠。漏洞公開時,pip-audit、Dependabot 與 Safety 都未能穩定標示受影響的 PyPI 版本,原因是 CVE 記錄指向 Google 的上游儲存庫,而非 PyPI 套件。GitHub 公告後來補上 PYSEC-2026-4322 與 PYSEC-2026-4323,但手動核對仍然必要。
這個漏洞的技術成因是什麼?
解碼器切換區塊類型時只回傳布林值,無法區分格式錯誤與資料不足,特製壓縮流可令位元組寫入八位元緩衝區之外,覆寫相鄰狀態。
問題集中在 c/dec/decode.c 的 DecodeBlockTypeAndLength 函式。Brotli 壓縮流由區塊組成,解碼器讀取時需要在不同區塊類型之間切換。修補之前,這個函式回傳單純的布林值,呼叫方無法分辨「格式錯誤」與「資料不足,需要更多輸入」兩種情況,快速路徑甚至直接忽略回傳值。
攻擊路徑由此展開。特製的壓縮流可以要求一個不可能發生的區塊切換,解碼器把它理解為需要更多輸入,於是持續把攻擊者提供的位元組附加到一個八位元內部緩衝區,寫入位置超出邊界,覆寫相鄰的解碼器狀態。缺陷同時對應 CWE-252 未檢查回傳值與 CWE-787 越界寫入。
上游修補的做法是改變回傳語義。google/brotli 的提交 0ed4268 連同 50a9b70、b418fa8 與 da98063,讓函式在遇到畸形資料時回傳硬性格式錯誤 BROTLI_DECODER_ERROR_FORMAT_BLOCK_SWITCH,錯誤碼為負十七。
修補之後還需要注意什麼?
安裝更新不足完成修補。執行中的程序會持有舊解碼器直至重啟,容器需以修補後基礎映像重建,鎖定檔約束亦要同步調高。
第一項是重啟。作業系統層的套件更新只替換磁碟上的檔案,已經載入記憶體的解碼器不會改變。長駐的服務、背景工作者、CI 執行器與伺服器無運行時都需要確實重啟,否則掃描報告會顯示已修補,實際行為仍然有缺陷。
第二項是映像重建。容器若沿用舊的基礎映像,重新部署只是把同一個問題搬去新機器。正確做法是以修補後的基礎映像重建,並在管線中加入版本檢查,避免快取層把舊映像留下。
第三項是版本約束。Python 專案要同步調整 requirements.txt、poetry.lock、uv.lock 與 Pipfile.lock 中的約束,否則下一次安裝會按舊約束取回受影響版本。PHP 的 brotli 擴充、Ruby 的 brotli gem 與 UEFITool 都已在十月二十四日推出捆綁 1.2.1 的版本,可供對照。
無法即時升級時,暫時緩解措施有限。Python 可以卸除 Brotli 與 brotlicffi,讓 urllib3、requests 與 aiohttp 退回 gzip 與 deflate,並移除 httpx[brotli] 之類的額外套件。入站方向的 br 請求內容可在代理、負載平衡器或 WAF 層拒絕,部分雲端供應商在公開當日已部署規則。Node.js 官方沒有提供替代方案,升級執行環境是唯一完整修法。
哪些專案至今仍未修好?
截至十月二十六日,.NET 執行階段、DuckDB、Apache Arrow 捆綁建置、7-Zip-zstd 與 woff2 仍未修補,系統更新無法覆蓋這些副本。
.NET 執行階段捆綁 1.1.0 版本,曝露面包括 BrotliDecoder、BrotliStream、HttpClient 的自動解壓,以及使用請求解壓中介軟體的 ASP.NET Core 應用。微軟已在議題 136422 確認正在處理。
資料分析工具同樣在名單之內。DuckDB 尚未修補,處理 Brotli 壓縮的 Parquet 檔案時受影響;Apache Arrow 的捆綁建置停留在 1.0.9,相關提交已獲批准但未合併。這兩個專案的使用場景多為批次處理外部資料,暴露程度取決於資料來源是否可信。
桌面與系統工具包括 7-Zip-zstd、p7zip、EDK II 與 NanaZip。前兩者在使用者開啟壓縮檔或 .br 檔案時觸發,屬於本機風險;EDK II 涉及壓縮的韌體區段,影響面在開機層。woff2 的 Makefile 建置捆綁 1.0.3,處理字型轉換時受影響,CMake 建置則連結系統函式庫,不受同一問題波及。
部分專案已經修好。Ruby 的 brotli gem 0.9.1、PHP brotli 擴充 0.21.1 與 UEFITool A76 都在十月二十四日推出捆綁 1.2.1 的版本。MuPDF 1.28.0 的 Brotli 分支早在四月已吸收上游修正,屬於公開前已完成修補的少數例子。
開發者現在應該怎樣檢查與處理?
先核對 Node.js 與 Brotli 版本,再以 pip show 與 pipdeptree 追查依賴,系統層查 libbrotli,最後檢查容器映像與 CI 執行器並重啟。
檢查的第一步是把版本攤開。Node.js 可用 node –version 判斷是否低於 22.23.4、24.22.1 或 26.12.1,再用 process.versions.brotli 讀出實際捆綁的解碼器版本。容器環境可以透過 docker run –rm –entrypoint node 加映像名稱,直接查詢映像內的版本。
Python 的追查需要雙向。pip show Brotli brotlicffi 顯示已安裝版本,pipdeptree 的 -r 參數則列出哪個套件把它拉進來,避免只升級直接依賴而漏掉間接引用。鎖定檔亦要一併以 grep 掃描,因為建置時使用的是鎖定版本,而非當前環境。
系統層與執行環境同樣要覆蓋。dpkg、rpm 與 apk 分別對應 Debian 系、RHEL 系與 Alpine 的查詢方式。生產、預備、CI 執行器、建置映像、無伺服器執行環境與開發者本機,都應納入同一份清單,優先處理會抓取不受控網址或接受 br 請求內容的服務。
處理順序建議由暴露面決定。對外提供服務、會解壓外部資料的節點先行,內部批次工具與桌面工具其次。每次修補後都要確認程序已重啟,並在監控中加入解碼錯誤的計數,以便在畸形壓縮流出現時及早察覺。
總結:這次漏洞帶來什麼教訓?
底層解碼缺陷會透過生態系的自動解壓行為擴散。回傳值的語義設計看似細節,實際決定記憶體安全,修補速度取決於能否盤點捆綁副本。
暴露程度方面,研究員已在官方未修改的 Node.js 24.22.0 Linux arm64 映像上,分別以抓取回應與 Express 請求內容兩種方式執行攻擊者提供的程式碼,過程需要多次嘗試,相關利用程式尚未公開。GitHub 在十月二十二日出現的公開概念驗證,據報只在刻意弱化的建置上達到程式碼執行,標準建置會當機並造成有限度的記憶體讀取。
攻擊時序方面,截至十月二十二日仍未觀察到實際攻擊。但概念驗證已經公開,這種狀態通常不會維持太久,尤其是任何會抓取網址的服務都能被動接收惡意回應。
設計層面的教訓更值得記錄。一個函式回傳布林值還是錯誤碼,看起來屬於介面風格問題,實際決定了呼叫方能否區分「資料不足」與「資料有問題」。這類語義模糊在記憶體不安全的語言中代價特別高,也是為何多個以其他語言重寫的解碼器不受影響,只有 C 實作會走到程式碼執行。
盤點能力則決定修補速度。這次的真正難點在於專案自帶的副本,而非主要專案本身:系統更新只覆蓋動態連結的程式,捆綁副本需要逐一追查。維護者若能掌握所有內嵌的第三方元件版本,就可在公告當日完成評估,而不是等掃描工具更新規則。