Node.js 2026年10月 セキュリティリリースの調査

2026-10-21(米国時間)に公開された 10 件の CVE の内容と、Web サーバ・CLI・Node.js 20 での影響、対処をまとめる

📅 作成: 2026-10-22 / 更新: 2026-10-25

⌂

Node.js の定例セキュリティリリースで、High 4 件を含む 10 件の脆弱性が修正された。本資料は、どの使い方のときに影響を受けるか(Web サーバ/HTTP クライアント/CLI)と、サポートが終了した Node.js 20 を使い続けている環境での判断材料をまとめる。

概要と修正版

系列状態サポート終了修正版
26.xCurrent2029-04-30修正 v26.12.1
24.xActive LTS2028-04-30修正 v24.22.1
22.xMaintenance LTS2027-04-30修正 v22.23.4
20.xEnd-of-Life2026-04-30修正なし

同梱の依存物も更新された。c-ares・Undici(全系列)、OpenSSL(24.x・22.x)、SQLite(22.x)、CA 証明書(侵害された eMudhra 中間 CA の除去。全系列)。

公式発表には回避策の記載が無く、対処は修正版への更新のみとされている。

脆弱性の一覧と仕組み

CVE深刻度対象系列内容影響する側
CVE-2026-111497High26 / 24 / 22Brotli デコーダの境界外書き込み。リモートコード実行のおそれ信頼できない Brotli データを展開する側
CVE-2026-105483High26 / 24全角数字で書いた IP で TLS のホスト名検証を回避(回帰バグ)TLS クライアント
CVE-2026-106069High26 / 24 / 22正規化で IP に化けるホスト名で TLS のホスト名検証を回避TLS クライアント
CVE-2026-105482High26 / 24 / 22dns.resolve* が NUL 文字以降を切り捨てる外部入力を名前解決する側
CVE-2026-105481Medium26 / 24 / 22keep-alive のソケットが、要求を送る前に届いた応答を処理するHTTP クライアント
CVE-2026-106072Medium26 / 24 / 22非正規の IPv4 表記で、証明書の DNS 名が IP の接続先を認証するTLS クライアント
CVE-2026-106074Medium26 / 24 / 22randomUUID()・randomInt() の乱数キャッシュが snapshot・SEA に焼き込まれ、全プロセスで同じ値になるsnapshot・SEA で配る CLI / サーバ
CVE-2026-106070Low26ZipFile がヘッダ検証失敗後に攻撃者の制御するバイト列を返すZIP を読む側
CVE-2026-106071Low26Permission Model のワイルドカードで、ファイルの許可が広がりすぎる--permission 利用時
CVE-2026-106073Low26 / 24 / 22fs.realpathSync で Permission Model を回避--permission 利用時

High 4 件の仕組み

CVE-2026-111497(Brotli)

不正な Brotli データの展開中に境界外書き込みが起きる。修正は Google の Brotli 本家のコミットの取り込みで、ブロック種別が 1 つしか宣言されていないストリームにブロック切り替えが現れたら即座に失敗させる。到達する API は zlib.brotliDecompress()・brotliDecompressSync()・createBrotliDecompress() と、応答の Content-Encoding: br を自動で展開する fetch()(Undici)。

fetch は要求していなくても展開する。クライアントが Accept-Encoding: identity を送っても、悪意あるサーバが br で返せばデコーダが動く。

Debian・Ubuntu のパッケージで入れた Node.js はシステムの libbrotli を使うことがあり、その場合の修正は libbrotli パッケージ側から来る。

別名 node2shell・HTTP2Shell

この脆弱性は、報道や各社のブログで node2shell (Node2Shell) と呼ばれ、その後 HTTP2Shell と呼び直された。どちらも CVE-2026-111497 の 1 件を指す別名で、別の脆弱性ではない。欠陥は Node.js 固有ではなく、Google の Brotli ライブラリ (1.0.3〜1.2.0) そのものにある。

日付 (2026年)出来事
2月OpenAI が欠陥を発見。Brotli 本家のソースでは直ったが、セキュリティの修正と認識されず、修正を含むリリースが出ないままだった
10月21日Node.js が修正版を公開。node2shell と呼ばれ始める
10月22日Python の Brotli パッケージも影響を受けると公表 (Python2Shell とも)
10月23日HTTP の圧縮を扱う多くのライブラリ・製品に及ぶとして HTTP2Shell と呼び直される。OpenAI も詳細を公開

深刻度は、CVE を採番した Google が CVSS 4.0 で 9.2 (Critical)、Node.js は High と評価している。Imperva は、改変していない公式の Node.js 24.22.0 のイメージで、任意のコードの実行を再現した (毎回は成功しない)。10月23日時点で、実際の攻撃は報告されていない。

Node.js で到達する経路

Node.js 以外

対象修正版
libbrotli (C)1.2.1 / 1.1.1 / 1.0.10
Bun1.4.4
Python (Brotli・brotlicffi)1.2.1・1.2.1.0
Envoy1.40.0・1.39.3・1.38.6・1.37.8
Istio1.29.9・1.30.6・1.31.2
curl・libcurl依存の libbrotli を更新。同梱しているアプリは作り直しが必要

.NET・PHP・Ruby・DuckDB・ClickHouse・7-Zip などにも Brotli を同梱する物があり、修正の状況は製品ごとに異なる。Apache・nginx は既定では要求本文の Brotli を展開しない。

CVE-2026-105483・CVE-2026-106069(TLS のホスト名検証)

tls.checkServerIdentity() が、IP の形をした接続先を、証明書の IP Address: ではなく DNS: 項目と照合してしまう。例: 0127.0.0.1 は url.domainToASCII() では 8 進として 87.0.0.1 と解釈されるが、OS は 127.0.0.1 へ接続する。DNS:87.0.0.1 の証明書で、別のマシンが本物として通ってしまう。

悪用には、クライアントが信頼する CA から IP の形をした DNS: 項目を持つ証明書を得る必要があり、条件は厳しい。接続先のホスト名を外部入力で決めるクライアントで問題になる。

CVE-2026-105482(dns.resolve)

c-ares が NUL を文字列の終わりとして扱う。'example.com\0.trusted.corp' を検査では .trusted.corp と見て許可しても、実際に問い合わせるのは example.com になる。dns.lookup()(OS のリゾルバ)は対象外で、dns.resolve*()・dns.reverse() が対象。

どの使い方で影響するか(Web・CLI)

今回の High は、多くが Node.js が外へ通信するとき(クライアント側)の問題である。「Web サーバか CLI か」より、信頼できない相手のデータを受け取るかで判断する。

使い方影響する CVE危険度の目安
Web サーバで、要求本文の Content-Encoding: br を展開する (Express 5・body-parser 2 の本文パーサは既定で展開する)111497高 外部から未認証で到達しうる
Web サーバが外部 API・利用者指定の URL へ fetch する(Webhook・プロキシ・OGP 取得・SSRF になりうる処理)111497・105481・TLS 3 件高 接続先を利用者が選べるなら特に
Web サーバが静的配信・API 応答だけ(圧縮は送るだけ、外へ通信しない)ほぼ無し低 圧縮して送る側は対象外
CLI がインターネットからダウンロードする(npm 系ツール・更新確認・スクレイピング)111497・TLS 3 件中 接続先が改ざん・なりすましされた場合
CLI が手元のファイルだけを扱う(ビルド・変換・lint)ほぼ無し低 外部の Brotli・ZIP を読まない限り
CLI・サーバを SEA や startup snapshot で配布している106074中 UUID・トークンが全プロセスで重複する
利用者入力のホスト名を dns.resolve* で検査する105482中 許可リストのすり抜け
--permission で権限を絞って動かす106071・106073低 Low
Web で使うかどうかより、外へ通信するかが効く。静的なサイトを返すだけのサーバより、外部 URL を取りに行く CLI のほうが今回の High に当たりやすい。

Node.js 20 を使っている場合

Node.js 20 は 2026-04-30 にサポートが終了した。公式は終了した系列を検証しないため、影響するかどうかの公式の判定は無く、修正版も出ない。

CVENode.js 20 での見込み(未検証の推定)
111497(Brotli)影響ありと見るべき 同じ Brotli ライブラリを同梱しており、修正は本家のコミット。20 にだけ欠陥が無い根拠は無い
106069・106072・105482・105481影響ありと見るべき 22 以降の全系列に共通する処理で、22 より古い 20 に入っていない根拠が無い
105483(全角数字)影響しない可能性 24・26 に入った回帰のため。ただし未確認
106074(SEA・snapshot)使っていれば要確認 20 では SEA が実験的機能

加えて、20 は 2026-04-30 以降に公開された過去の脆弱性(2026 年 6 月・7 月のリリース分)も修正されていない。今回だけを見て「当たらない」と判断しても、未修正の穴は積み上がっている。

20 のまま安全にする方法は無い。22 は 2027-04-30 で終わるため、移るなら 24(2028-04-30 まで)を勧める。

Node.js 20 から 26 で変わること・非互換

20 から 26 へは 6 メジャー分(21〜26)の変更をまたぐ。公式の移行ガイドは v20→v22 と v22→v24 の 2 本があり、v24→v26 のガイドは見つからなかった。まず 24 へ上げ、続けて 26 を試す二段の移行が現実的。

主要な版の推移

Node.jsV8NODE_MODULE_VERSIONnpmOpenSSL
20.011.31159.63.0 系
22.012.412710.53.0 系
24.013.613711.03.5
26.014.614711.123.5

非互換になりやすい点

分類変更版影響と対処
ネイティブアドオンNODE_MODULE_VERSION が 115→147。V8 の C++ API も変更。ビルドに C++20 が必要毎版要再ビルド node-gyp 製の依存はすべて作り直し。prebuild の対応を確認。Node-API 製なら影響は小さい
TLSOpenSSL 3.5 の既定セキュリティレベル 2。2048 bit 未満の RSA・DH 鍵、224 bit 未満の ECC 鍵、RC4 を拒否24接続失敗 古い証明書の社内サーバや機器への TLS 接続が通らなくなる
Windows.bat・.cmd を spawn で直接起動できない(CVE-2024-27980 対策)20 の後期・22要修正 shell: true を付ける
Windows32 bit(x86)版の提供終了23動かない 32 bit Windows は移行先が無い
モジュールimport assertions(assert { type: 'json' })を削除22要修正 with { type: 'json' } に書き換え(20 でも動く書き方)
モジュールrequire() で ESM を読める(require(esm))が既定22.12・23挙動変化 ERR_REQUIRE_ESM を捕まえて分岐するコードが別の経路に入る
削除された APIcrypto.createCipher・createDecipher22実行時エラー createCipheriv へ
削除された APIutil.isString() 等の util.isX() 群・util._extend・util.log23実行時エラー typeof・Array.isArray・Object.assign へ。古い依存パッケージで踏みやすい
削除された APItls.createSecurePair()、fs.Dirent.path(→parentPath)、OutgoingMessage._headers24実行時エラー codemod あり
削除された APIfs.F_OK 等(→fs.constants.F_OK)、SlowBuffer、assert.CallTracker25実行時エラー
削除された APIhttp.Server の writeHeader()、_stream_readable 等の内部モジュール、--experimental-transform-types26実行時エラー 古いストリーム系の依存に注意
非推奨(警告)url.parse()(実行時警告)、組み込み punycode、module.register()21〜26警告 WHATWG URL へ
CLI フラグ--experimental-permission → --permission24要修正 起動スクリプトの書き換え
同梱物Corepack を同梱しなくなった25要修正 corepack enable 前提の CI・Dockerfile は npm i -g corepack 等が必要
テストテストランナのサブテストを自動で await。既定のレポータが spec23・24挙動変化 node:test の書き方次第
出力文字列util.inspect・assert のメッセージ、TLS のエラーコード名の綴り修正23〜26挙動変化 出力やエラーメッセージを文字列で比べるテストが落ちうる
SQLitenode:sqlite で外部キー制約が既定で有効、二重引用符の文字列リテラルが無効23挙動変化
プラットフォームLinux armv7(32 bit)のバイナリ提供終了、macOS 13.5 以上24動かない 古い Raspberry Pi 等

追加された機能(上げると使えるもの)

移行の進め方

  1. node --trace-deprecation を付けて 20 のまま動かし、非推奨の警告を洗い出す(削除される API の多くは 20 でも警告が出る)
  2. 公式の codemod(移行ガイドに記載)で機械的に書き換えられる箇所を直す
  3. ネイティブアドオンの依存を洗い出す(npm ls で node-gyp・prebuild を使う物)
  4. 24 で全テストを流す。TLS 接続先の鍵長と、Windows の .cmd 起動を重点的に確かめる
  5. 24 で安定したら 26 を試す
サポート期限の資料間の食い違い。nodejs/release の表では 20 の終了は 2026-04-30。nodejs.org の過去リリースのページには別の日付が出るという報告もあった。どちらの日付でも、20 がすでにサポート外である点は変わらない。

対処

優先順位

  1. 外部と通信するサーバ・ツールから上げる。Brotli を展開する Web サーバ、fetch で外部へ取りに行く処理
  2. Node.js 20 の環境は 24 へ移す。移すまでの間は、下の暫定策で露出を減らす
  3. SEA・snapshot で配っている物は、更新した Node.js で作り直す。実行環境の Node だけを上げても直らない
  4. Node.js を内蔵する物を確かめる。Docker のベースイメージ(node:20 等のタグを固定していないか)、Electron、CI のランナ、ディストリのパッケージ(libbrotli も)

移行までの暫定策(恒久対策ではない)

const net = require('node:net');
const url = require('node:url');

// 非正規の IP 表記(0127.0.0.1 など)を拒否する
function isNonCanonicalIP(host) {
	return !net.isIP(host) && net.isIP(url.domainToASCII(host)) !== 0;
}

版の確認

node -v
node -p "process.versions.brotli + ' / ' + process.versions.openssl"

出典

関連: shell-quote のコマンド注入

Node.js 本体ではなく、npm パッケージ shell-quote の脆弱性。シェルのコマンド文字列を組み立てる (quote())・分解する (parse()) ためのパッケージで、多くのツールに間接依存として入っている。

CVE深刻度対象修正版公開
CVE-2026-9277High CVSS 3.1: 8.1 (4.0: 9.2)1.1.0〜1.8.3修正 1.8.42026-05-22
CVE-2021-42740Critical CVSS 3.1: 9.81.7.3 未満修正 1.7.32021-10

CVE-2026-9277 の仕組み

quote() は、演算子を表すオブジェクト ({ op: ';' } など) の .op を、正規表現 /(.)/g で 1 文字ずつエスケープしていた。JavaScript の . は改行に一致しないため、.op の中の改行がエスケープされずに残る。POSIX シェルは改行をコマンドの区切りと読むので、改行の後ろが別のコマンドとして実行される。

const { quote } = require('shell-quote');
const s = quote([{ op: ';\nid' }]);   // 改行がそのまま残る
require('child_process').execSync(s, { shell: '/bin/sh' });   // id が実行される

1.8.4 では .op を正しい演算子の一覧と照合し、それ以外を受け付けない。

影響する場合

Node.js の版とは関係が無い。Node.js 20 でも 26 でも、パッケージを上げれば直る。

対処

npm ls shell-quote        # どの依存から入っているか
npm update shell-quote    # 宣言の範囲内なら lockfile の更新で 1.8.4 になる

間接依存の範囲が 1.8.4 を許さないときは、package.json の overrides (pnpm は pnpm.overrides) で上書きする。Debian・Ubuntu のパッケージ (node-shell-quote) は、各ディストリの更新 (Debian は DSA-6300-1) で直る。

根本的には、外部の値を含むコマンドをシェル経由で実行しない。execFile・spawn に引数を配列で渡せば、シェルを通らず、この種の注入は起きない。

出典