如何在 2 分鐘內檢查 Node.js 版本是否受 Brotli RCE 漏洞 CVE-2026-111497 影響
¿Tu versión de Node.js es vulnerable al RCE de Brotli? Compruébalo en 2 minutos
作者整理了檢查 Node.js 是否受 Brotli 解碼器 RCE 漏洞 CVE-2026-111497 影響的方法,並列出 22.x、24.x、26.x 的修補門檻,分別為 22.23.4、24.22.1 和 26.12.1。
文章對照 `node -v`、Brotli 常數及 `process.versions.brotli` 的檢查限制,並整理更新 runtime、重建容器映像和重啟程序的修補流程。
立即執行。如果你的版本低於表格所列的最低版本,你便會受到 Node.js Brotli RCE 漏洞(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 及更早版本 | 已終止支援(EOL):未修補,亦未評估 | 沒有修補程式 | 遷移至 24 或 26 |
如要再次確認,可直接查詢二進制檔:
node -p "require('node:zlib').constants.BROTLI_DECODER_ERROR_FORMAT_BLOCK_SWITCH"
修補程式加入了這個常數。我們在三個受支援版本系列的九個 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 套用修補程式,但沒有修改回報的版本,因此這個欄位無法用來判斷系統是否已受保護。
簡而言之(TL;DR)
| 問題 | 答案 |
|---|---|
| 這是甚麼? | Node.js 內置 Brotli 解碼器的越界寫入漏洞,亦稱為 Node2Shell 或 HTTP2Shell |
| 影響 | 程序崩潰、記憶體資料外洩或遠端程式碼執行(RCE) |
| 嚴重程度 | Node.js 評為高;NVD評為 9.8 嚴重(CVSS 3.1) |
| 是否正遭積極利用? | 是。CISA 的 KEV 目錄於 2026 年 10 月 27 日將此漏洞列入其中 |
| 是否需要驗證? | 根據 NVD 的攻擊向量及Akamai 的分析,不需要 |
| 哪些用戶會受影響? | 會解碼不受其控制的 Brotli 資料的應用程式:Express 5 的請求主體、fetch() 的回應,或直接呼叫 node:zlib
|
| 解決方法 | 更新執行環境、重新建置容器映像檔,並重新啟動程序 |
| 有沒有臨時緩解措施? | 官方安全公告沒有列出任何措施。以下詳述的臨時措施可降低受影響程度,但無法涵蓋所有情況 |
ELLIO 在其感測器中回報了漏洞攻擊流量,距離修補程式發布僅五天,即 10 月 26 日。截獲的有效負載(payload)針對採用 x86-64 架構的 Node.js 24.22.0。雖然該有效負載的最後階段在 ELLIO 的測試中失敗,但該公司確認,標準版 24.22.0 組建可遭執行程式碼。CISA 將 11 月 10 日定為美國聯邦機構採取行動的期限,並將此漏洞在勒索軟件中的使用情況標記為「未知」。此外,Akamai 的遙測數據顯示,截至 10 月 26 日,受監察網絡中仍有 61 % 正在執行受影響版本。
Node2Shell 的運作方式:Brotli 漏洞的影響
Node2Shell 是 Node.js 執行環境內置 Brotli 解碼器中的越界寫入漏洞。解碼器處理格式錯誤的壓縮資料流時,會寫入超出內部八字節緩衝區的範圍,破壞程序記憶體。因此,漏洞可能導致伺服器崩潰、記憶體資料外洩,或在攻擊者控制下遠端執行程式碼。
根本原因在於錯誤分類不當:遇到無效的區塊變更時,解碼器把情況解讀為「需要更多輸入資料」,而非「這些資料已損毀」,因此持續將位元組寫入小型內部緩衝區。
問題出在 Google 的 Brotli 程式庫,影響版本由 1.0.3 至 1.2.0,因此並非只限於 Node.js。Node.js 團隊透過 cherry-pick 上游提交 0ed4268a,在 版本 24.22.1 及相應版本中修正了其實作。漏洞由 OpenAI Codex Security 的研究員 Khai Tran 和 Sylvie Mayer 發現並通報。
你的依賴項分析工具不會偵測到這個問題,因為解碼器屬於執行環境的一部分,而非 npm 套件。事實上,在我們的測試項目中,Express 5 執行於有漏洞的 Node.js 24.15.0 之上,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,「解壓縮失敗」 |
Express 4 express.json()
|
4.22.3 (body-parser 1.20.9) | 不會 | 415,不支援的編碼方式 |
Koa @koa/bodyparser
|
6.1.0 | 會,預設情況下 | 500,記錄中出現 BrotliDecoder 錯誤 |
Fastify 搭配 @fastify/compress
|
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本身 |
有兩項結果尤其值得留意。fetch() 即使請求傳送了 Accept-Encoding: identity,仍會解碼 Brotli,因為它依據回應標頭,而非請求的內容。你的應用程式呼叫的任何外部伺服器,都可能令解碼器處理其回應,使 webhook 測試服務、連結預覽產生器或 URL 匯入工具面臨風險。此外,單憑 400 回應並不能保證安全:Express 5 和不含外掛的 Fastify 都會傳回這個代碼,但只有 Express 5 會執行解碼器。請務必檢查錯誤訊息。
使用以下三個命令找出可能受影響的地方:
# Direct decoder use
grep -rnE "brotliDecompress|createBrotliDecompress" src/
# Parsers that decode request bodies
npm ls express body-parser @koa/bodyparser @fastify/compress
# Base images and pinned versions
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 執行器及開發環境中,執行本文開頭所列的檢查。要查閲各版本分支最新的安全版本:
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 20 的生命週期終止(EOL)計劃,並盡快遷移。
如果今天無法更新,Node.js 的緩解措施
這些替代方案可以為你爭取時間,但都不能取代更新。
-
在邊緣端(edge)拒絕
br。 在反向代理伺服器或負載平衡器中封鎖Content-Encoding: br,並確保比對時不區分大小寫,且涵蓋gzip, 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()沒有標準的停用機制,因此建議透過白名單及防火牆輸出規則,限制它可以連線的主機。 - 縮小影響範圍。 對 Node.js 服務進行網絡分段,並限制記憶體傾印檔(core dumps)的讀取權限;ELLIO 提醒,系統崩潰可能會令記憶體中的敏感資料透過這些檔案外洩。
請留意 ELLIO 最初發現的模式:大量突發請求使用 Brotli 編碼的本文,目的是探測哪些伺服器會解壓縮這些內容。若 JSON API 出現含有 Content-Encoding: br 的異常流量,或 worker 程序崩潰次數突然增加,便應立即調查。
確認你的 API 是否經受住更新:讓你的 Agent 進行驗證
執行環境版本變更可能會影響 TLS、DNS 和 HTTP 的行為;事實上,十月的更新在這三方面都引入了變更。不要手動核對這些差異:請交由你的開發 Agent 在變更前後執行 Apidog CLI,並回報任何差異。
npm install -g apidog-cli@latest
apidog login --with-token <YOUR_TOKEN>
npx -y skills add https://apidog.com # pick your agent when prompted
向你的 Agent 發出以下指示:
Run Apidog test suite <id> in project <id> against staging and save the
results to reports/before. After I upgrade Node.js, add a test case for
POST /orders that sends the plain text "not-valid-brotli" with
Content-Encoding: br, asserts a 4xx status, then checks GET /health
returns 200. Rerun the suite to reports/after and list every request
whose result changed.
Agent 將執行以下命令:
apidog run --test-suite <id> --project <id> -e <envId> -r cli,junit --out-dir reports/before
apidog cli-schema get test-case-create
apidog cli-schema validate test-case-create --file case.json
apidog test-case create --project <id> --file case.json
apidog run --test-case <caseId> --project <id> -e <envId> -r cli,json
apidog run --test-suite <id> --project <id> -e <envId> -r cli,junit --out-dir reports/after
這個額外的測試案例是健康檢查,而不是漏洞利用:請求主體是純文字;在我們的測試中,易受攻擊和已修補的版本都出現相同的填充(padding)錯誤。即使如此,它仍能找出一個實際問題:我們在 Koa 的測試中遇到 500 錯誤,原因是解碼失敗未有被捕捉;而公開 API 理應回應 4xx。只可在 staging 環境執行。
run 指令參考詳細列出所有可用選項,而CI/CD 指南則示範如何在你的 pipeline 中加入相同的 JUnit 格式執行方式。Apidog 免費方案最多支援 4 位用戶。
為何 11 月 11 日是關鍵日期
Node.js 項目已預先公佈新一輪更新,適用於 26.x、24.x 和 22.x 分支,預定於 2026 年 11 月 11 日或緊接其後推出;相關漏洞再次被評為最高級別的高危漏洞。目前尚未公開相應的 CVE。請參閲我們的 11 月發佈指南,瞭解如何作好準備。
請立即套用 Brotli 修補程式,不要等到新一輪發佈才處理。漏洞詳情已公開,目前亦有活躍的漏洞利用流量;此外,你今週進行的更新也可作為下一次更新的演練:使用相同的清單、重建相同的映像檔,並執行相同的一組測試。PHP2Shell 和 React2Shell 也出現過完全相同的模式。
常見問題(FAQ)
CVE-2026-111497 正遭受活躍攻擊嗎? 是。CISA 於 2026 年 10 月 27 日以 Google Brotli 條目將其加入已知遭利用漏洞目錄(KEV),而 ELLIO 早於 10 月 26 日已記錄到漏洞利用流量。
它屬於高危還是嚴重級別? 兩種評級都成立。Node.js 將其評為高危。NVD 在 CVSS 3.1 中給予 9.8 分,評級為嚴重;Google 在 CVSS 4.0 中給予 9.2 分。規劃應對措施時,請以漏洞已遭受活躍攻擊為前提。
我使用 Express 4,是否安全? 傳入的請求主體不會傳到解碼器,但透過 fetch() 或 axios 處理的回應,以及直接呼叫 node:zlib 的情況則會。此外,這個版本亦修正了另外九個漏洞(CVE),因此無論如何都應更新。
Node.js 20 會受影響嗎? Node.js 項目不會評估已達支援週期終止(EOL)且不再接收修補程式的版本。請視 20.x 和 25.x 為尚未修補、亦未經評估的版本,並遷移至仍受支援的版本。
除了 Node.js,其他技術也會受影響嗎? 會。GitHub 安全公告列出 PyPI 套件 brotli 和 brotlicffi;多家供應商亦已公佈 Bun 1.4.4 和 .NET 的修補程式。請分別更新每個解碼器,因為更新 Node.js 只會保護其內置副本。
來源:DEV Community · dev.to

