Node.js 專案於 2026 年 10 月 21 日發布安全更新,同時修補 26.x、24.x 與 22.x 三條仍在支援中的版本線,共處理 10 個 CVE,其中 4 個為高風險(High)、3 個中風險(Medium)、3 個低風險(Low)。官方公告明確指出,三條版本線各自修補的最高嚴重度都是 High。
CyberQ 觀察,這次最值得注意的是 Brotli 解碼器的越界寫入漏洞(CVE-2026-111497),官方標題直接寫明「可導致遠端執行程式碼」。另外有三個與 TLS 憑證主機名驗證相關的漏洞,影響以 IP 位址連線的 HTTPS 用戶端。
CyberQ 建議,有在跑 Node.js 的服務,都應該盡快升級。
Node.js 修補版本如下:
| 版本線 | 修補版本 | 支援狀態(發布當日) | 支援終止 |
|---|---|---|---|
| 26.x | v26.12.1 | Current,預定 10 月 28 日轉為 LTS | 2029 年 4 月 30 日 |
| 24.x | v24.22.1 | Maintenance LTS(10 月 20 日起) | 2028 年 4 月 30 日 |
| 22.x | v22.23.4 | Maintenance LTS | 2027 年 4 月 30 日 |
20.x 已於 2026 年 4 月 30 日結束支援(EOL),更早的版本也都已 EOL。官方公告特別提醒,每次安全更新發布時,EOL 版本一律視為受影響,而且不會再有修補。
一、為什麼這次算嚴重
1. Brotli 解碼器越界寫入(CVE-2026-111497,High)
Brotli 是 Google 開發的壓縮格式,廣泛用於 HTTP 傳輸(Content-Encoding: br)。Node.js 內建的 zlib 模組附帶 Brotli 函式庫,zlib.brotliDecompress()、zlib.createBrotliDecompress() 都會用到它。
官方對這個漏洞只有一句說明,Brotli 解碼器的越界寫入可導致遠端執行程式碼。越界寫入屬於記憶體破壞類漏洞,攻擊者若能讓程式解壓一段特製的 Brotli 資料,就可能讓程式當掉,最壞情況是執行任意程式碼。
修補方式是從上游 Brotli 專案 cherry-pick 一個 commit(0ed4268a4401)。該 commit 的說明是「更早偵測並回報無效的資料流」,修改集中在解碼器處理區塊切換(block switch)的路徑:遇到格式不合法的區塊切換時立即回報錯誤,不再繼續往下處理。Node.js 也因此新增了一個錯誤碼 BROTLI_DECODER_ERROR_FORMAT_BLOCK_SWITCH,程式若有依錯誤碼分流,升級後可能會看到這個新值。
誰會受影響? 凡是會解壓「不可信來源」Brotli 資料的程式都在範圍內,常見情境包括:
- 用
fetch()抓取外部網址。Node.js 的fetch()會自動解壓Content-Encoding: br的回應,惡意或遭入侵的伺服器可以直接送出特製內容。 - 反向代理、爬蟲、Webhook 接收端、API 閘道等會轉送或解壓外部回應的服務。
- 接受使用者上傳
.br壓縮檔,或接受Content-Encoding: br請求主體的伺服器。
也就是說,這不是只有「特殊用途」才會碰到的功能。一個單純呼叫外部 API 的後端服務,只要對方回傳 Brotli 壓縮內容,就會走到這段程式碼。
2. TLS 憑證主機名驗證可被繞過(CVE-2026-106069、CVE-2026-105483,High)
HTTPS 連線時,用戶端除了檢查憑證是否由可信任的 CA 簽發,還要確認憑證上的名稱和實際連線的對象一致,這一步就是主機名驗證。這兩個漏洞都出在「連線目標是 IP 位址」的情境:
- CVE-2026-106069:主機字串「寬鬆地」可以解讀成 IP 位址時,驗證可被繞過。影響 26.x、24.x、22.x。
- CVE-2026-105483:IP 位址以全形數字書寫時,驗證可被繞過。影響 26.x 與 24.x。修補 commit 的標題是「修正 IP 正規化的回歸問題」,可見這是先前某次修改引入的問題,22.x 沒有受到影響。
同一批修補還有一個中風險的 CVE-2026-106072:非標準寫法的 IPv4 主機名,會讓憑證上的 DNS 名稱(DNS SAN)被拿來驗證 IP 端點。官方把它和 CVE-2026-106069 合併在同一個修補「拒絕非標準形式的 IP 位址」中處理。
IP 位址有不少「非標準」寫法,例如 0x7f.1、017700000001、省略中間段的 127.1,再加上全形數字這類 Unicode 字元。如果驗證邏輯和實際連線時對位址的解讀不一致,攻擊者就有機會拿一張合法但不相符的憑證通過檢查。實際攻擊需要攻擊者能影響連線目標字串,並且處於可攔截流量的位置(中間人),因此風險高低取決於應用程式怎麼組出連線網址。
官方目前沒有公開這三個漏洞的細節。原則上,以 IP 位址直接連 HTTPS 服務、而且 IP 字串來自外部輸入(例如使用者設定的 Webhook 網址、服務探測工具)的程式最需要注意。
3. dns.resolve 遇到 NUL 字元會截斷名稱(CVE-2026-105482,High)
dns.resolve() 系列函式在名稱中含有 NUL 字元(\0)時,會把名稱截斷在 NUL 之前。若應用程式先檢查完整字串(例如確認網域在允許清單內),再把它交給 dns.resolve(),實際查詢的卻是截斷後的名稱,兩邊看到的東西就不一樣了。這類「檢查與使用不一致」的問題,常被用來繞過 SSRF 防護或網域白名單。修補後,dns.resolve() 會直接拒絕含有 NUL 位元組的輸入。三條版本線都受影響。
二、其他中、低風險漏洞
CVE-2026-106074(Medium):randomUUID 在快照與 SEA 中重複產生相同值。 crypto.randomUUID() 與 crypto.randomInt() 為了效能,會預先產生一批亂數放在快取裡。問題是這份快取會被一起存進使用者自建的啟動快照(startup snapshot)與單一執行檔應用程式(SEA)。結果是每次從同一份快照或執行檔啟動的行程,前幾個「隨機」值都一模一樣。若這些值被拿來當作工作階段 ID、權杖或任何需要不可預測的識別碼,後果相當嚴重。這個漏洞光升級 Node.js 還不夠,快照與 SEA 執行檔必須用新版重新建置。
CVE-2026-105481(Medium):keep-alive 連線處理到請求送出前就寫入的回應。 HTTP 用戶端重複使用 keep-alive 連線時,可能把伺服器在請求送出「之前」就寫進連線的資料,當成這次請求的回應來處理。若伺服器端可被操控,可能造成回應錯置。修補 commit 標題是「確保 agent 的資料防護在沒有 I/O 時也能運作」。
CVE-2026-106070(Low,僅 26.x): Node.js 的 ZipFile 在 ZIP 標頭驗證失敗後,仍可能回傳攻擊者控制的前綴位元組。修補方式是不再快取無效的 ZIP 本地標頭。
CVE-2026-106071(Low,僅 26.x)與 CVE-2026-106073(Low,三條版本線): 兩個都是權限模型(Permission Model,--permission 旗標)的問題。前者是萬用字元路徑授權時查找邏輯有誤,授權範圍超出預期;後者是 fs.realpathSync() 沒有經過權限檢查,可被用來繞過檔案系統限制。沒有啟用權限模型的程式不受這兩個漏洞影響。
三、完整 CVE 一覽
| CVE | 嚴重度 | 受影響版本線 | 說明 |
|---|---|---|---|
| CVE-2026-111497 | High | 26.x、24.x、22.x | Brotli 解碼器越界寫入,可遠端執行程式碼 |
| CVE-2026-106069 | High | 26.x、24.x、22.x | 主機可寬鬆解讀為 IP 時,TLS 憑證主機名驗證可被繞過 |
| CVE-2026-105483 | High | 26.x、24.x | IP 以全形數字書寫時,TLS 憑證主機名驗證可被繞過 |
| CVE-2026-105482 | High | 26.x、24.x、22.x | dns.resolve 遇 NUL 字元截斷名稱 |
| CVE-2026-105481 | Medium | 26.x、24.x、22.x | keep-alive 連線處理到請求送出前就寫入的回應 |
| CVE-2026-106072 | Medium | 26.x、24.x、22.x | 非標準 IPv4 主機名讓 DNS SAN 可驗證 IP 端點 |
| CVE-2026-106074 | Medium | 26.x、24.x、22.x | randomUUID/randomInt 亂數快取被存進快照與 SEA |
| CVE-2026-106070 | Low | 26.x | ZipFile 標頭驗證失敗後回傳攻擊者控制的位元組 |
| CVE-2026-106071 | Low | 26.x | 權限模型萬用字元查找造成檔案系統過度授權 |
| CVE-2026-106073 | Low | 26.x、24.x、22.x | fs.realpathSync 可繞過權限模型 |
四、相依套件也一併更新
這次安全更新同時升級了幾個內附的相依套件,以處理已公開的漏洞:
| 套件 | 26.x | 24.x | 22.x |
|---|---|---|---|
| c-ares(DNS) | 1.34.9 | 1.34.9 | 1.34.9 |
| undici(fetch、HTTP 用戶端) | 8.12.2 | 7.30.1 | 6.29.1 |
| OpenSSL | 不在本次清單 | 3.5.9 | 3.5.9 |
| SQLite | 不在本次清單 | 不在本次清單 | 3.53.4 |
| 根憑證 | NSS 3.131.1 | NSS 3.131.1 | NSS 3.131.1 |
根憑證更新的原因,官方公告寫的是「eMudhra 中繼 CA 遭入侵」。同一批更新還加入了「拒絕包含已封鎖公鑰的憑證鏈」的 TLS 修改,讓 Node.js 能在信任鏈中直接擋下特定公鑰。官方沒有說明兩者的關聯,也沒有公開事件細節,筆者截稿時也查不到 eMudhra 方面的公開說明。
五、該怎麼做
1. CyberQ 建議先儘快確認目前版本。
node -v
低於 v22.23.4、v24.22.1、v26.12.1 的都需要升級。20.x 以下的版本沒有修補可以裝,只能升到 22.x 以上。
2. 升級所有執行環境,不只是開發機。 實際部署時,Node.js 往往同時存在於好幾個地方:
- Docker 映像:
node:22、node:24等標籤會指向新版,但已經建好的映像不會自己更新,必須重新 pull 基底映像並重建。寫死小版本(例如node:24.22.0)的 Dockerfile 要手動改版本號。 - CI/CD:檢查
.nvmrc、package.json的engines、GitHub Actions 的node-version是否鎖在舊版。 - Serverless 平台:雲端業者的 Node.js 執行環境通常會自動套用修補,但時間不一,建議到平台公告確認。
- 桌面應用程式:Electron 等框架內嵌自己的 Node.js,需要等框架與應用程式各自發布更新。
3. 重建快照與 SEA 執行檔。 有使用 --build-snapshot 或單一執行檔應用程式(SEA)的專案,升級後一定要用新版 Node.js 重新產生,否則 CVE-2026-106074 的問題仍然存在。
4. 暫時無法升級時的緩解措施。 以下做法只能降低風險,不能取代升級:
- 對外發出的請求把
Accept-Encoding設為不含br。這只能讓正常的伺服器不回傳 Brotli,惡意伺服器仍可無視;fetch()收到br回應時一樣會自動解壓。存取不可信來源時,可改用http/https模組自行處理回應,遇到Content-Encoding: br直接中止。 - 不要解壓來源不可信的
.br檔案或 Brotli 請求主體,在入口處(反向代理、WAF)先擋下。 - 避免直接以 IP 位址連線 HTTPS 服務;若必須使用 IP,先把外部輸入的 IP 字串正規化並嚴格驗證格式,只接受標準的十進位點分寫法。
- 交給
dns模組的名稱先檢查是否含有 NUL 字元。
5. 訂閱官方安全公告。 Node.js 的 nodejs-sec 郵件清單 只發安全相關公告,信件量很少,建議負責維運的人都訂閱。
於受影響的都是最常用的功能
CyberQ 分析,這次安全更新的嚴重度不在於漏洞數量,而在於受影響的都是最常用的功能,解壓縮、HTTPS 連線、DNS 查詢。幾乎所有 Node.js 後端服務都會碰到其中至少一項,而 Brotli 漏洞更是被官方直接標示為可遠端執行程式碼。
目前官方只公開了漏洞標題,技術細節與概念驗證程式碼尚未釋出。依過往經驗,修補公開後,攻擊者可以比對前後程式碼找出漏洞位置,從修補到出現實際利用的時間往往不長。建議趁細節公開之前完成升級,特別是直接面對網際網路、或會處理外部回應的服務。








