前言

2026 年 10 月 21 日,除了 PHP 的緊急安全更新,Node.js 也釋出新版本,處理 10 項漏洞:4 項 High、3 項 Medium、3 項 Low。最高等級為 High,其中 CVE-2026-111497 涉及 Brotli 解碼器的越界寫入,官方列出的影響包含遠端程式碼執行(Remote Code Execution,RCE)。Node.js 官方安全公告

對維運與後端團隊來說,應先確認實際執行的版本,再看程式用到哪些功能。接收外部壓縮資料、呼叫外部 HTTP/HTTPS 服務、使用自訂啟動快照,以及依賴 Permission Model 限制檔案操作,是這篇要逐一說明的盤點方向。

查核日期:2026-10-22,台灣時間 UTC+8。 本文解讀 10 月 21 日的官方公告、發布紀錄與公開修補程式碼;程式碼檢閱與利用重現是不同層次的證據,本文未執行漏洞 PoC。封面為 AI 生成的概念插畫,以折疊材料越過檢查框呈現邊界檢查的意象。

一、先確認更新版本:22.23.4、24.22.1、26.12.1

本次三條受支援分支都已有安全版本。若仍使用本次更新之前的版本,應依所屬分支套用修補,並確認正式環境的程序已完成更新。

正在使用的分支 本次安全版本 2026-10-22 的分支狀態
Node.js 22.x 22.23.4 Maintenance LTS
Node.js 24.x 24.22.1 Maintenance LTS
Node.js 26.x 26.12.1 Current;官方排程預定 10/28 進入 LTS

版本依據:22.23.4 發布說明、24.22.1 發布說明、26.12.1 發布說明。支援狀態依據:Node.js 官方發布排程。

這三個版本是本次事件的修補基準;之後若同一分支已有更新的安全版本,應一起評估後續更新。下節會列出各 CVE 適用的分支,不能把「某個分支需要更新」解讀成「每項漏洞都存在於該分支」。

還在使用 Node.js 20、18 或 25 呢?

依查核時的官方排程,這些分支都已停止支援。Node.js 的安全政策要求使用者將 EOL 分支視為需要處理的風險,遷移至受支援版本;它們沒有出現在本次修正版清單,不構成安全保證。發布排程、官方 EOL 風險提醒

這是維護狀態上的判斷,仍需尊重功能差異:例如只在 26.x 列出的 ZipFile 問題,不能直接套用到所有舊版。對發行版、商業維護或嵌入式產品提供的版本,則要取得供應商對具體 CVE 的修補確認。

二、10 項 CVE 總表:先看功能,再看分支

以下依官方安全公告整理。這裡使用 Node.js 專案公布的 High/Medium/Low 等級,不自行補上公告未提供的數值型 CVSS 分數。

CVE 等級 官方描述的問題 公告列出的受影響支援分支
CVE-2026-111497 High Brotli 解碼器越界寫入,可能導致 RCE 22、24、26
CVE-2026-105482 High dns.resolve 截斷含 NUL 字元的名稱 22、24、26
CVE-2026-105483 High IP 使用全形數字時,可繞過 TLS 憑證主機名稱驗證 24、26
CVE-2026-106069 High 主機名稱經寬鬆解析成 IP 時,可能繞過 TLS 主機名稱驗證 22、24、26
CVE-2026-105481 Medium Keep-alive socket 處理了請求送出前就已寫入的回應 22、24、26
CVE-2026-106072 Medium 非標準 IPv4 表示法,使 DNS SAN 可被用來驗證 IP 端點 22、24、26
CVE-2026-106074 Medium 亂數快取被保存進自訂 startup snapshot/SEA,造成程序重播相同亂數值 22、24、26
CVE-2026-106070 Low ZIP 標頭驗證失敗後,ZipFile 仍可能回傳攻擊者控制的前置位元組 26
CVE-2026-106071 Low Permission Model 的 wildcard 路徑查找可能授予過多檔案權限 26
CVE-2026-106073 Low fs.realpathSync 缺少 Permission Model 檢查 22、24、26

來源:Node.js 10/21 安全公告。部分發布紀錄會把共用修補的 CVE 合併在同一列;個別漏洞的嚴重度,應以安全公告逐項列出的等級為準。

接下來用實際資料流說明:外部資料如何抵達這些功能,以及哪些前提會影響風險判斷。

三、Brotli RCE:對外下載資料的服務,也要盤點

Brotli 是壓縮格式,HTTP 常以 Content-Encoding: br 表示回應內容經過 Brotli 壓縮。Node.js 提供的 Brotli 解壓縮能力位於 node:zlib,底層會執行原生解碼程式。

官方確認到哪裡?

官方將 CVE-2026-111497 定義為解碼器越界寫入,並列出 RCE 影響。公開修補可見,deps/brotli/c/dec/decode.c 加強了無效 block switch 的錯誤處理:遇到不合法資料時,回傳明確的格式錯誤並中止後續處理,避免把異常狀態繼續當成正常解碼流程。Brotli 官方修補

這能說明修補在加強哪個邊界。至於完整 RCE 如何建立、對不同平台的可靠度與特定服務能否被利用,還需要額外證據,不能從一筆解壓縮錯誤就下結論。

為什麼只搜尋 brotliDecompress 不夠?

Node.js 內建 fetch() 以 Undici 為基礎。檢閱更新前的 Node.js 24.22.0 原始碼,可以看到它在處理適用的 HTTP 回應時,會依 Content-Encoding 選擇解碼器;遇到 br,會建立 zlib.createBrotliDecompress()。24.22.0 的 fetch 原始碼、fetch 官方文件

因此,需要盤點的不只直接呼叫解壓縮 API 的程式,也包括下載遠端內容的功能:例如網址預覽、資料同步、爬取公開資訊、匯入外部檔案,以及代抓圖片或文件。這些是依資料流提出的工程盤點情境;是否能走到漏洞,仍取決於實際 HTTP client、回應處理與執行版本。

flowchart LR
    A[使用者提供網址或外部資料來源] --> B[Node.js 服務發出 HTTP 請求]
    B --> C[對端回傳 Brotli 壓縮內容]
    C --> D[HTTP client 選擇解壓縮流程]
    D --> E[Node.js 原生 Brotli 解碼器]
    E --> F[受影響版本需處理記憶體安全風險]

圖示只呈現一條可能的可達路徑,沒有證明任意網址都能成功觸發 RCE。它提醒我們:入口 WAF 看到的可能只是一般網址參數,真正進入解碼器的內容,來自伺服器後續收到的外部回應。

在完成更新前,應依業務需求限制不必要的外部資料來源與解壓縮入口。這類限制可以縮小暴露範圍,修補仍應落在實際執行的 Node.js。

四、TLS 與 DNS:連線目的地的解讀需要一致

這一組漏洞適合從「應用程式以為自己連到誰」來理解。尤其是由 Node.js 主動連線到其他服務的程式,需要檢查使用者輸入、名稱解析與憑證驗證之間是否一致。

DNS NUL 截斷:檢查過的字串,可能不是實際查詢的名稱

NUL 是值為零的字元。在 JavaScript 字串裡,它可以存在於字串中間;底層若將名稱當成 C 字串處理,卻可能在第一個 NUL 就視為結束。

Node.js 的公開回歸測試使用了類似這個概念的輸入:

1
2
JavaScript 字串:example.com\u0000.trusted.corp
底層可能查詢:example.com

上例依官方測試註解整理,本文沒有執行 DNS 查詢。修補將 resolver 的輸入檢查改為拒絕 NUL,涵蓋 callback 與 Promise 路徑。DNS 修補與回歸測試

若應用先用完整字串做網域允許清單判斷,之後才交給底層解析,這種解讀差異就可能影響安全決策。實際能否進一步形成 SSRF 等問題,仍取決於應用的驗證與連線流程;不能假設所有 URL parser 都接受同一輸入。

TLS 名稱驗證:IP 與網域名稱有不同的比對規則

TLS 憑證的 SAN(Subject Alternative Name)可以列出 DNS 名稱或 IP 位址。連線目的地若是 IP,就需要依 IP 身分比對;名稱經不同正規化方式處理後,也應與實際連線目標一致。

本次修補涉及兩個相關方向:

  • 全形數字等表示法,可能讓原始主機名稱與正規化後的 IP 被分到不同驗證路徑。
  • 非標準數字主機名稱,可能被 domainToASCII() 與作業系統 resolver 解讀成不同位址。

檢閱 lib/tls.js 的公開修補,可以看到團隊先調整 IP 的判斷,再明確拒絕「原本不被視為標準 IP、正規化後才變成 IP」的主機名稱,避免對不是實際對端的位址驗證憑證。全形 IP 相關修補、非標準 IP 拒絕邏輯

這類繞過針對的是身分驗證與解析規則。 風險評估需要交代攻擊者能否影響目的地主機名稱、對端、憑證或連線環境,不能直接推論所有 HTTPS 流量都已被解密。

升級後,原先依賴非標準 IP 寫法的內部整合也可能開始失敗。應修正位址與憑證設定,保留 TLS 驗證,不要以關閉驗證來處理相容性問題。

五、另外幾項漏洞,影響哪些使用方式?

Medium 與 Low 的功能範圍也值得確認,特別是客製化執行環境與工具型應用。

HTTP keep-alive:回應應該屬於實際送出的請求

CVE-2026-105481 涉及 Node.js 的 HTTP client Agent 重用閒置連線。對端提前送來的資料,若已抵達作業系統 socket buffer,但事件迴圈尚未處理,就可能在連線交給下一個請求後,被當成該請求的回應。

公開修補在重用 socket 前檢查是否已有待讀資料,必要時銷毀該連線,改用其他連線。盤點重點是使用這類 Agent 的對外 HTTP 呼叫,以及誰能影響對端資料與連線狀態。HTTP Agent 官方修補

啟動快照:相同的亂數快取被複製到不同程序

CVE-2026-106074 涉及 crypto.randomUUID()、randomInt() 的快取,被保存在使用者建立的啟動快照與 SEA(Single Executable Application,單一執行檔應用)中。從相同快照啟動的程序,可能重播尚未使用的相同亂數值。

修補會在使用者序列化回呼之後、反序列化回呼之前清掉相關快取。這個條件需要和一般啟動 Node.js 程序分開判斷;單純把相同程式碼部署成多個容器,不足以證明這個快照問題存在。亂數快取官方修補

flowchart TD
    A[自訂快照建立時保留未使用的亂數快取] --> B[產生同一份快照產物]
    B --> C[程序甲載入快照]
    B --> D[程序乙載入快照]
    C --> E[可能取到相同的快取亂數值]
    D --> E
    F[修補後在快照生命週期重設快取] --> G[避免沿用這批快取值]

如果產品會產生 snapshot 或 SEA,應納入產物的重建與發布。應用自己在建置階段產生、再存成常數的識別碼,也需要另外檢查;執行階段清理內部快取,無法替應用重新產生已保存的資料。

ZIP 與 Permission Model:對應特定 API 和配置

另外三項 Low 漏洞可以用下面的功能條件盤點:

功能 需要注意的使用條件 公開修補可支持的影響
26.x 的 ZipFile 讀取檔案型 ZIP,且遇到 local header 與 central directory 驗證失敗後繼續操作的路徑 先快取再驗證的順序可能留下不完整狀態,後續讀取回傳非預期的前置內容
26.x 的 Permission Model wildcard 使用 Permission Model,並以 wildcard 定義檔案存取範圍 radix tree 查找可能讓較短路徑得到不應有的授權
fs.realpathSync 啟用 Permission Model,並依賴檔案讀取範圍限制 缺少預先檢查,可能被用來探查授權範圍外的路徑是否存在

來源:ZIP 驗證順序修補、wildcard 查找修補、realpathSync 權限檢查。

Permission Model 是由 --permission 等配置啟用的執行期資源限制。這些問題需要結合程序內可執行的程式碼、所呼叫的 API 與授權規則評估,不能直接把「權限檢查繞過」寫成任意外部使用者已能讀取主機所有檔案。Permission Model 官方文件

六、Express、Next.js、前端建置與 CLI,各自怎麼看?

框架名稱只能提供盤點方向。實際風險要沿著「執行版本 → 呼叫功能 → 外部可控制資料」往下確認。

以下是依上述功能條件整理的工程判斷,並非官方公布的受害產品名單:

專案情境 優先確認的項目
Express、Fastify、NestJS API 實際 Node.js 版本、外部 HTTP 呼叫、解壓縮、resolver 與權限配置
以 Node.js 執行的 Next.js/Nuxt SSR SSR 服務與背景工作用的 runtime,以及渲染或代理時讀取外部資料的路徑
背景工作、爬蟲、同步服務 下載來源、HTTP client、自動解壓縮與長連線重用;沒有公開 HTTP 入口也可能接觸不可信資料
靜態前端或 Hexo 等建置產物 建置機/CI 的 Node.js 與其資料來源;另外確認正式站是否還有 Node.js 程序
Node.js CLI 與內部工具 工具實際啟動的 Node.js,及它是否會下載、解壓或處理外部檔案
自訂 snapshot/SEA 產物使用的執行階段與建立流程,是否需要重建及重新發布

例如,一個只提供靜態檔案的正式網站,不能因為它用 Node.js 建置,就被當成公開暴露同一個 Node.js 解碼器;但建置程序本身仍可能接觸外部資料,需要獨立維護。

有哪些知名網站已遭攻擊?

本文查閱的 Node.js 官方安全公告與發布紀錄,沒有提供可據以列出「知名網站已成功遭利用」的名單。採用 Node.js 的公司名稱、同日發生的服務維護,以及某個 CVE 具備 RCE 能力,都是不同層次的資訊。

因此,前一篇 PHP2Shell 整理的日本網站停站公告,也不能直接拿來作為這批 Node.js 漏洞的受害名單。後續若有營運方或事件調查的第一手證據,再依其實際部署與調查結果補充。

七、實務處理順序:更新真正執行工作的那一套 Node.js

版本管理器、服務管理器、容器與 CI,可能各自使用不同的 Node.js。修補完成的判斷,應落在實際程序與部署產物。

1. 記錄版本、執行檔與內建依賴

下面是唯讀盤點指令範例,應在對應的服務或容器環境執行;本文未在讀者的正式環境執行。它會顯示目前啟動的 Node.js 與依賴版本,不是漏洞利用測試:

1
node -p 'JSON.stringify({ node: process.version, executable: process.execPath, brotli: process.versions.brotli, undici: process.versions.undici, openssl: process.versions.openssl }, null, 2)'

process.versions 可列出 Node.js 及其依賴版本,內建 Undici 的版本也可透過這裡確認。process 官方文件、fetch 與內建 Undici 說明

這次 Brotli 修補以 cherry-pick 方式納入 Node.js,判斷時應對照 Node.js 安全版本與供應商公告,不能只用某個依賴的版本字串推定修補狀態。

2. 更新套件或映像檔,再完成程序輪替

依部署方式確認更新真正生效的位置:

  • 服務程序:確認 systemd、PM2 或其他管理器使用的執行檔,更新後完成程序輪替。
  • 容器:確認新映像包含修補,重新部署,並確認仍接流量的舊副本已退出。
  • CI 與建置機:更新工作流程指定的 Node.js、基底映像與建置工具環境。
  • snapshot/SEA:評估並重建包含舊執行階段或快照資料的發布產物。

套件相依性掃描與執行階段盤點應分別完成。package.json 的 engines、lockfile,或一次 pnpm audit 結果,都不足以單獨證明正式程序的 Node.js 已更新。

3. 把附帶依賴與憑證更新一起納入驗證

本次發布同時更新 c-ares、Undici、憑證資料,以及部分分支的 OpenSSL、SQLite。憑證更新包含 eMudhra intermediate CA compromise 相關處置。升級後應觀察外部服務連線,必要時與對方修正憑證鏈。官方安全公告的依賴更新清單、24.22.1 發布紀錄、22.23.4 發布紀錄

內建 fetch() 的實作與另外安裝在專案裡的 HTTP 套件,也要分別辨認;新增或更新一個名為 Undici 的套件,不足以證明 Node.js 內建的原生 Brotli 已完成修補。

4. 依使用功能做發布後確認

建議將這些項目加入部署檢查,實際範圍依服務功能調整:

使用功能 發布後確認方向
外部 HTTP/HTTPS 正常請求、逾時與錯誤處理、連線重用、憑證與標準 IP 位址
壓縮回應或匯入檔案 正常 Brotli 資料與格式錯誤的處理,以及程序是否穩定
DNS/自訂 resolver 合法輸入正常工作,含控制字元的輸入會在合適邊界被拒絕
自訂快照 重建後的產物版本,以及不同程序啟動後的識別碼與亂數使用方式
Permission Model/ZipFile 原先允許的操作可繼續,拒絕路徑與格式錯誤不會留下可重用的不完整狀態

這是建議的部署確認清單,不代表本文已替任何正式服務完成測試。若已有異常程序終止、可疑對外連線或其他利用跡象,應同步保存紀錄並啟動事件調查;升級成功不能取代對先前行為的調查。

總結

這次 Node.js 安全更新,最值得帶回團隊的觀念是:風險要看外部資料如何抵達執行階段的功能。 後端自己發出的 HTTP 請求、自動解壓縮、名稱解析,以及快照產物,都應納入盤點。

處理時先確認實際版本,套用所屬分支的安全更新,再依功能安排檢查。對 RCE、驗證繞過與亂數重複等問題,則分別說明必要條件、官方已確認的能力與仍待查證的事件結果。

延伸閱讀

主要來源

各功能修補已在對應段落直接連結;以下是版本與 API 行為的主要查核入口。