Node.js 十月漏洞深度解析:Brotli RCE、TLS 驗證繞過與 10 項修補

2026 年 10 月 21 日,Node.js 安全更新列出 10 項具名漏洞,最高嚴重度為 High。其中包含 Brotli 解壓縮可能造成遠端程式碼執行、TLS 主機名驗證繞過,以及啟動快照重複使用亂數快取等問題。
這次值得仔細讀,因為風險入口不只在「有人連進你的網站」。後端向外抓取資料、重用 HTTP 連線、打包單一執行檔,都可能涉及不同的漏洞條件。下面依官方公告、發行紀錄與公開修補程式碼,逐項說明應該檢查什麼。
資料截點:2026 年 10 月 21 日 23:50,台灣時間。 修正版在查證時已可取得。文中的風險情境是依公開資訊與程式碼路徑分析,不是已確認受害案例,也不是本文實測成功的攻擊結果。
先看更新重點與修補版本
Node.js 官方安全公告列出的修補版本如下。請在自己使用的分支套用此次修補版本,或該分支後續已包含修正的版本。
依官方條列項目整理,這 10 項分為 4 項 High、3 項 Medium、3 項 Low;公告另外包含相依元件與憑證資料更新,因此「10 項」不代表整個發行內容只有這十個問題。
Node.js 18、20 等舊分支已在官方維護狀態表列為 EOL。使用這些版本的系統,需要規劃遷移到受支援分支;若依靠供應商的延長維護與回補修正,則要取得對應安全公告與套件修訂證據。
十項漏洞,各自影響哪些分支?
以下分支範圍依官方安全公告整理,並非宣稱每個舊版、每種設定都有相同利用方式。公告未逐一列出所有漏洞最早在哪個版本引入。
| 漏洞 | 等級 | 公告列出的分支 | 核心問題 |
|---|---|---|---|
| CVE-2026-111497 | High | 22/24/26 | Brotli 解碼器越界寫入,可導致遠端程式碼執行 |
| CVE-2026-105482 | High | 22/24/26 | DNS 解析名稱遇到 NUL 字元時可能遭截斷 |
| CVE-2026-105483 | High | 24/26 | 全形數字表示的 IP 可能繞過 TLS 主機名驗證 |
| CVE-2026-106069 | High | 22/24/26 | 可被寬鬆解析為 IP 的主機名稱,可能繞過 TLS 驗證 |
| CVE-2026-105481 | Medium | 22/24/26 | Keep-alive 連線可能把請求送出前的資料當成回應 |
| CVE-2026-106072 | Medium | 22/24/26 | 非標準 IPv4 名稱可能讓 DNS 型憑證身分誤用於 IP 端點 |
| CVE-2026-106074 | Medium | 22/24/26 | 啟動快照或 SEA 保存亂數快取,使程序重播相同值 |
| CVE-2026-106070 | Low | 26 | ZIP 標頭驗證失敗後,後續讀取可能回傳攻擊者可控的前綴資料 |
| CVE-2026-106071 | Low | 26 | Permission Model 的萬用字元路徑比對可能放行過多權限 |
| CVE-2026-106073 | Low | 22/24/26 | fs.realpathSync 缺少必要的 Permission Model 檢查 |
同一個修補提交可能處理兩個 CVE,不能把提交數當成漏洞數。TLS 的 106069 與 106072 就由同一份公開修補處理,但官方對兩者的嚴重度標示不同。
Brotli RCE:對外抓資料的後端也需要盤點
CVE-2026-111497 最需要優先關注的,是程式是否會把不可信的 Brotli 壓縮資料交給有漏洞的解碼器。官方認定可能導致遠端程式碼執行;不能進一步推成任何 Node.js 網站都能被同一個 HTTP 請求攻破。
Brotli 上游修補調整了區塊切換的錯誤處理,讓無效資料流更早被辨識,並把錯誤往呼叫端傳遞。重點是原生解碼器處理異常輸入的安全性,不只是 JavaScript 多寫一個例外捕捉就能解決。
容易漏掉的入口,是你的後端主動向外連線。修補版內建的 Undici fetch 原始碼可看到,回應的內容編碼為 br 時會使用 Brotli 解碼器。
因此,以下是值得盤點的情境:抓取使用者提供的網址、匯入遠端內容、串接可被攻擊者控制的回應來源,以及處理外來壓縮檔。這是根據資料流推導的檢查範圍,不代表每一種操作都已證實可成功利用。
只把頁面壓縮成 Brotli 後送出去,也不能直接當成解碼漏洞已可觸發的證據。應追的是「誰提供壓縮內容、誰負責解開」,而不是只搜尋設定裡有沒有 Brotli 這個字。
DNS 與 TLS:名稱解析和身分驗證必須一致
這組問題的共同線索,是同一份輸入,經過不同元件後被理解成不同的名稱或位址。
NUL 截斷:驗證的字串與實際解析的名稱可能不同
CVE-2026-105482 涉及 DNS 名稱中的 NUL,也就是值為零的字元。修補程式碼把 DNS 解析入口的檢查改為拒絕含 NUL 的字串,涵蓋 callback 與 Promise 形式的解析流程。
如果應用程式先用完整字串做存取限制,後面的解析層卻只處理截斷後的部分,就可能出現驗證落差。不過,是否進一步形成 SSRF 等應用層風險,還要看輸入來源、限制規則與後續連線方式,不能只憑這個 CVE 名稱下結論。
TLS 繞過:憑證有效,不等於就是你要連的主機
105483、106069、106072 涉及全形數字、非標準 IP 表示法與憑證名稱比對。HTTPS 驗證除了確認憑證鏈,也需要確認對端身分符合預期主機。
從 26.12.1 的最終 TLS 程式碼可以看到:原字串不是標準 IP、經名稱轉換後卻變成 IP 的情況,會被拒絕;標準 IP 則走 IP 位址的憑證比對。這是在收緊解析與身分驗證的邊界。
高優先盤點對象包含:可輸入自訂網址的代理、遠端匯入、URL 預覽,以及依賴自訂主機名規則的整合服務。實際攻擊仍需要可達的資料路徑與適合的憑證、端點等條件,不能寫成所有 HTTPS 連線都已失去保護。
修補後若原本接受的非標準名稱開始報錯,應修正輸入與憑證設定,保留 TLS 身分驗證。
HTTP 連線與亂數快照:Medium 為什麼仍值得處理?
Keep-alive 的問題,在回應與請求的對應關係
CVE-2026-105481 涉及 HTTP Agent 重用閒置連線時,尚未送出新請求,就已經有資料等在作業系統的 Socket 緩衝區中。
修補提交說明,舊有資料防護依賴事件迴圈進行 I/O 輪詢,可能還沒看到這批資料。新版在重用前主動確認待讀狀態,必要時丟棄連線。
對使用連線池的 API 整合而言,這是回應是否真的屬於本次請求的正確性問題。不要直接把它改稱為已證實的伺服器端 request smuggling,或自行宣稱能跨帳號取得資料;那需要額外證據。
亂數快照:複製了啟動狀態,也可能複製尚未用完的亂數
CVE-2026-106074 的條件更特別:應用使用啟動快照,或帶有相關快照狀態的單一執行檔 SEA,而 randomUUID()/randomInt() 尚未用完的快取被保存下來。多個從相同狀態啟動的程序,可能重播相同值。
修補程式碼在序列化與還原的關鍵時點清除快取。這不是「所有 Node.js 亂數都壞了」,而是特定打包與啟動方式把本應重新取得的狀態一起帶走。
若應用把這些值當成必須不可預測的權杖,安全影響可能放大;若用作識別碼,也可能遇到碰撞。這是應用情境分析,不能當成已發生權杖外洩的證據。
使用這類產物時,要以修補後的工具鏈重新產生快照與執行檔。SEA 官方文件說明它包含 Node.js 執行檔與應用資料;只更新主機上另一份 Node.js,不能證明既有 SEA 已換掉內嵌的舊執行環境。
Permission Model 與 ZIP 的風險要放回使用情境
CVE-2026-106071 與 106073 分別涉及萬用字元授權範圍,以及 fs.realpathSync 的權限檢查。後者的修補明確提到,先檢查解析後的輸入路徑,避免利用後續路徑走訪去探查授權集合之外的路徑。
使用 –permission 的工具、檔案處理流程或自動化系統,應檢查這兩項;但不能把 realpath 這個問題擴寫成已能任意讀取所有檔案內容。
更根本的界線在 Permission Model 官方文件:它限制受信任程式意外存取未授權資源,不提供對惡意程式碼的安全隔離保證。會執行不可信程式碼的 AI 工具或服務,仍需要作業系統層的隔離設計。
CVE-2026-106070 則限官方列出的 26.x ZIP 功能。修補改為先驗證 ZIP 本地標頭與中央目錄的一致性,再保存標頭與內容位置,避免失敗後留下可被後續讀取誤用的快取狀態。重點是失敗路徑與重試,不宜自行升級描述為任意檔案寫入。
npm audit 與執行環境更新,分別解決什麼?
npm audit檢查專案相依套件的已知漏洞資訊。它的結果不能直接證明正在執行程式的 Node.js 二進位檔、內建原生函式庫與憑證資料都完成這次更新。
從三份發行紀錄還能看到:內建 Undici 分別更新到 22.x 的 6.29.1、24.x 的 7.30.1、26.x 的 8.12.2;22.x、24.x 更新 OpenSSL,22.x 另更新 SQLite,並有 c-ares 與憑證相關更新。
這些是官方發行內容的核對線索。專案另外安裝的 Undici,與 Node.js 內建給 fetch 使用的版本,需要分別檢查;修改 package-lock.json 不會自動替換已部署的 Node.js 執行檔。
官方安全公告也列出與 eMudhra 中繼 CA 事件相關的憑證更新。修補範圍因此包含執行環境攜帶的信任資料,不只應用程式碼。
先修哪些系統,以及怎樣才算修補完成?
可以先用下面順序盤點。這是依資料可達性整理的維運優先順序,不是官方對個別服務的受害判定。
- 會解開外來 Brotli 資料,或向使用者可控來源抓取內容的服務:先確認原生解碼器與實際 Node.js 已修補。
- 接受自訂主機名、URL,或有出站存取限制的服務:檢查 DNS、TLS 正規化與驗證路徑。
- 大量使用 HTTP Agent 連線池的整合服務:更新後驗證回應對應、錯誤處理與重試。
- 產生啟動快照、SEA 或使用 Permission Model 的工具:重新建置產物,並測試實際使用的限制與啟動流程。
- 只在建置階段使用 Node.js、上線後由其他伺服器提供靜態檔的網站:先盤點 CI 與建置環境;是否有對外 Node.js 程序,要依實際架構確認。
確認版本時,請在實際服務的容器、程序啟動環境或平台工作負載裡執行,而不只看自己電腦的終端機:
1 | node -p "JSON.stringify({ node: process.version, executable: process.execPath, versions: process.versions }, null, 2)" |
這個命令只讀取版本與執行檔資訊,不會升級系統。欄位內容會依版本與編譯方式不同;使用發行版回補套件時,仍須核對供應商的修訂版與公告。
部署完成還要確認:執行中的程序已重啟或替換、所有副本都用到修補版本、快照與 SEA 已重新產生、關鍵連線與解壓縮流程通過測試。容器標籤變動或 CI 顯示成功,本身不能證明舊工作負載已經離開服務流量。
同日的 PHP 事件與網站停機案例也提醒了執行環境更新的份量。不過,這篇列出的 Node.js 程式碼風險,不能拿 PHP 的停機名單當成已遭利用的證據。是否暫停服務,應由實際暴露路徑、修補可用性與部署能力判斷。