Node.jsの11月セキュリティリリース、HTTP/2とTLS中心に16件
目次
TL;DR
影響
Node.js 26.x・24.x・22.xで16件(HIGH 7件、MEDIUM 6件、LOW 3件)。HTTP/2サーバーのプロセス停止やメモリ漏えい、HTTPSサーバーが別接続のメモリデータをレスポンスに含める問題、mTLS(クライアント証明書認証)の回避が中心。
対応
v26.13.1、v24.22.3、v22.23.5への更新。v25やv20などサポート終了済みの系列は修正版なし。
実機で確かめた範囲
v26.12.1とv26.13.1を同じPCで比べ、5件は修正前の不具合と修正後の解消を確認、6件は差を確認できず。
影響を受けやすい条件
HTTP/2サーバーを稼働させている、HTTPSサーバーでリクエスト本文を受け取る、ホスト名ごとにクライアント証明書の条件を変更している環境。
2026年11月11日、Node.jsが26.x、24.x、22.xの各リリースラインでセキュリティリリースを公開した(Node.js — Wednesday, November 11, 2026 Security Releases)。
修正は16件で、深刻度HIGHが7件、MEDIUMが6件、LOWが3件ある。
3週間前の10月21日にもセキュリティリリースが出たばかりで、そのときの内容はNode.js v24.22.1で直ったHIGH脆弱性4件を修正前と比べたに書いた。
手元の検証環境(M4 Mac mini)のNode.jsを node -v で確かめるとv26.12.1で、10月の修正版だが今回の修正は入っていない。
今回の16件は、7件がHTTP/2、5件がTLSとHTTPS関連で、残りの4件がプロキシ設定のエラーメッセージ、zlib、パーミッションモデルに関わる。
HTTP/2は1本の接続の中に「ストリーム」と呼ぶ複数のリクエストとレスポンスを同時に流せるプロトコルで、Node.jsでは node:http2 モジュールで提供されている。
修正された16件と対象のリリースライン
対象の列は、セキュリティアドバイザリ記載の影響を受けるラインと同じにした。
22.xが対象外のものは、HTTP/2関連の4件とTLSサーバーの停止の1件。
| CVE | 内容 | 深刻度 | 対象 |
|---|---|---|---|
| CVE-2026-123863 | HTTP/2のセッション終了時に内部の確認処理が失敗し、サーバーのプロセスが落ちる | HIGH | 26.x、24.x |
| CVE-2026-123864 | HTTPSサーバーが、他の接続の分を含むメモリの中身をリクエスト本文として扱ってしまう | HIGH | 26.x、24.x、22.x |
| CVE-2026-123865 | HTTP/2の切断処理中にプロトコルエラーを起こされると、セッションが片付けられずメモリとファイルディスクリプタ(OSの管理枠)が残り続ける | HIGH | 26.x、24.x、22.x |
| CVE-2026-123866 | HTTP/2のストリームを終了したとき、解放済みのレスポンス用メモリが相手に送られる | HIGH | 26.x、24.x、22.x |
| CVE-2026-126051 | TLSサーバーのハンドシェイク(接続確立処理)中の処理で、捕捉されない例外が起きてプロセスが落ちる | HIGH | 26.x、24.x |
| CVE-2026-123872 | TLS 1.2の再ネゴシエーション(暗号化の再交渉)で接続先のホスト名を変えられ、クライアント証明書の認証を回避できる | HIGH | 26.x、24.x、22.x |
| CVE-2026-123874 | HTTP/2の respondWithFile() で、書き込み待ちの間にストリームをリセットされると解放済みメモリを使う | HIGH | 26.x、24.x |
| CVE-2026-123828 | HTTP/2の session.altsvc() に長いオリジンを指定すると、確保した領域の1バイト先に書き込む | MEDIUM | 26.x、24.x |
| CVE-2026-123867 | ホスト名ごとにクライアント証明書のCA(認証局)を分けた設定が効かず、既定のCAで認証が通る | MEDIUM | 26.x、24.x、22.x |
| CVE-2026-123868 | HTTP/2で読み出されずに溜まった受信データが maxSessionMemory の上限に数えられない | MEDIUM | 26.x、24.x |
| CVE-2026-123871 | ERR_PROXY_INVALID_CONFIG のエラーメッセージにプロキシの認証情報が出る | MEDIUM | 26.x、24.x、22.x |
| CVE-2026-123877 | https.Agent が、CAの指定が違うリクエストの間でTLSセッションを使い回す | MEDIUM | 26.x、24.x、22.x |
| CVE-2026-123875 | HTTP/2の respondWithFile() でのダウンロードを取り消されると、NULLポインタ(未割り当ての参照)を参照して落ちる | MEDIUM | 26.x、24.x、22.x |
| CVE-2026-123829 | パーミッションモデルで、macOSとWindowsの再帰の fs.watch が、読み取りを禁止したファイルの名前を通知する | LOW | 26.x、24.x、22.x |
| CVE-2026-123862 | zlibで、相手へ送るデータを圧縮している途中に deflate.params() を呼び、その処理待ちの間に相手の切断などでストリームが破棄されると、捕捉できない例外が出る | LOW | 26.x、24.x、22.x |
| CVE-2026-106071 | パーミッションモデルで、ワイルドカード指定のファイルアクセス許可が必要以上に広く与えられる | LOW | 26.x、24.x、22.x |
パーミッションモデルは、--permission を付けて起動したときにファイルやネットワークへのアクセスを制限する仕組み。通常起動ならCVE-2026-123829と106071の影響は受けない。
CVE-2026-123829で漏れるのはファイル名と更新日時だけで、中身は漏れない。macOSかWindowsで、監視しているフォルダーの中に別のプロセスなどがファイルを作った場合に起きる。
CVE-2026-106071は、--allow-fs-read=/etc/passwd.* のようなワイルドカード指定が /etc/passwd 自体も読めるようにしてしまう問題。その指定が同種別で唯一のルールになっている場合など、許可ツリーの最上位にあるときに起きる。この問題は10月のリリースでも修正されていて、そのときのアドバイザリでは対象が26.xだけだったが、今回は24.xと22.xも対象に含まれる。
CVE-2026-123862は、アドバイザリでも deflate.params() を相手への送信中に呼ぶ使い方は珍しいと書かれている。
アドバイザリの報告者欄では、16件のうち5件(CVE-2026-123863、123864、123874、123875、123862)を「Chris Rohlf in collaboration with Claude and Anthropic Research」が報告している。
修正の大半はマッテオ・コリーナ(Matteo Collina)氏が担当していた。
自分のアプリが影響を受けるかの目安
どの脆弱性も、外部から攻撃されるにはサーバーが特定の機能を使っていることが前提になる。
アドバイザリ記載の前提条件を使い方ごとに整理した。
| 使い方 | 関係する主なCVE | アドバイザリに書かれた前提 |
|---|---|---|
node:http2 でサーバーを立てている | 123863、123865、123866、123868 | 既定設定のHTTP/2サーバーで成り立つ。123863は、リクエスト本文完了前にハンドラーが応答する一般的な構成で起きる |
HTTP/2でファイルを respondWithFile() / respondWithFD() で返している | 123874、123875 | その2つの関数でファイルを返していることだけが条件 |
HTTP/2の session.altsvc() に、:authority ヘッダーのような外部入力を渡している | 123828 | 長いが文法的に正しい :authority を1回送るだけで起きる |
node:https でサーバーを立て、リクエスト本文を受け取る | 123864 | 本文受信完了前に res.flushHeaders() を呼び、あとで res.end() に空でないデータを渡すハンドラーがあること。漏洩したメモリを確認するには、本文をそのまま返すか保存するルートが別に必要。暗号化しない node:http は対象外 |
TLSサーバーで SNICallback、OCSPRequest、newSession、resumeSession のイベントや server.addContext() を使っている | 126051 | どれか1つを使っていれば、細工したハンドシェイク(接続確立処理)でプロセスを落とされる |
| ホスト名ごとにクライアント証明書(mTLS)の条件を変えている | 123872、123867 | 123872はTLS 1.2の再ネゴシエーションでホスト名を変える。123867は server.addContext(host, { ca }) でCAだけを指定し、サーバー証明書を入れていないとき |
https.Agent で ca を指定したリクエストと指定しないリクエストを混ぜている | 123877 | ca を省略した場合と ca: [] を指定した場合が同じ接続として再利用される |
| 環境変数などでプロキシのURLに認証情報を含めている | 123871 | プロキシの設定が不正なときに出るエラーメッセージに、ユーザー名とパスワードが含まれる |
フロントエンドのビルドやCLIツールにだけNode.jsを使っていて外向きのWebサーバーを公開していない場合、関係する脆弱性はほぼない。
それでも修正版が出たあとに古い版を残しておく理由はないので、更新しておくのが確実。
HTTPSサーバーが他の接続のメモリを返す問題(CVE-2026-123864)
今回特に影響が大きいのがこの問題で、認証のないクライアントが他の接続(別ユーザーの通信)のメモリを取得でき、その中にはTLS秘密鍵や他ユーザーのリクエストデータも含まれるとアドバイザリに書かれている。
HTTPSサーバーでは、受け取った暗号化データをTLS層で復号してから、HTTPパーサー(llhttp)へ渡してリクエストを読み取る。
リクエスト本文を読み取っている最中のコールバックから0バイトの書き込みが発生すると、TLS層がその場で次の暗号化データを復号してしまい、本文処理中のパーサーが入れ子で呼び出される。この入れ子の呼び出しによって、解放済みのメモリを使う「use-after-free」や確保範囲外を読む「範囲外読み取り」が起きる。その結果、サーバーは解放済みのメモリや関係のないメモリの中身を、そのリクエストの本文として扱ってしまう。
解放済みのメモリとは、プログラムが使い終わってOSやランタイムに返却した領域のこと。返却後もデータは即座に消去されないため、直前の別接続で使ったTLS秘密鍵や他ユーザーのリクエストデータが残っている場合がある。それが本文として扱われると、本文をそのまま返すルートや保存するルートを通じて攻撃者へ漏洩する。
修正は tls,http: prevent nested parser execution from empty writes というコミットで、0バイトの書き込みからパーサーが入れ子で動かないようにしている(CHANGELOG_V24)。
HTTP/2のストリーム終了と解放済みメモリ(CVE-2026-123866、123874)
HTTP/2では、1本の接続の中で複数のリクエストとレスポンス(ストリーム)が並行して流れる。相手が RST_STREAM という制御フレームを送ると、そのストリームだけを途中で打ち切れる。
CVE-2026-123866は、レスポンスの書き込みが詰まって待機している間にストリームが終了すると、レスポンス用のメモリは解放されるのに、その参照がまだ使われてしまう問題だった。解放済みのメモリや関係のないメモリの中身が本来の応答データの代わりにソケット(ネットワーク接続)へ書き出され、他の通信のデータが相手に漏れることがある。
アドバイザリによると、26.xと24.xでは、26.9.0と24.22.0で入ったウィンドウサイズ(相手から受信できるデータ量の上限)の変更のあと、相手側の操作だけでこの終了を起こせるようになった。22.xを含むすべてのラインでも、ストリームごとのアイドルタイムアウトのようにアプリ側がストリームを終了する場合には同じ不具合が発生する。
CVE-2026-123874も同じ種類の問題で、respondWithFile() などでファイルを返している途中にストリームをリセットされると、書き込み待ちの処理が解放済みストリームのオブジェクトを参照してしまう。こちらは情報漏洩ではなくサーバープロセスのメモリ破壊として報告されている。
ホスト名ごとのmTLSの回避(CVE-2026-123872、123867)
mTLS(相互TLS)は、サーバーだけでなくクライアント側も証明書を提示してお互いを認証する方式。1台のサーバーで複数のホスト名を受ける場合、クライアントは接続開始時にSNI(Server Name Indication: 接続先ホスト名通知)を送り、サーバーはそのSNIを見て使用する証明書や認証条件を決める。
CVE-2026-123872は、TLS 1.2の再ネゴシエーション(接続を維持したまま暗号化パラメータを再交渉する機能)で、クライアントがSNIを別のホスト名に変更できた問題。たとえば管理用ホスト名だけクライアント証明書を必須にしているサーバーで、一般ホスト名として認証を通したあと、再ネゴシエーションで管理用ホスト名へ切り替えると、最初の認証状態のまま管理用ホストへアクセスできてしまう。修正では、再ネゴシエーションやTLSセッション再開時にSNIを変更できないようにし、条件によってはセッション再開自体を止めた(CHANGELOG_V24)。
CVE-2026-123867は、server.addContext(host, { ca: protectedCA }) のようにホスト名ごとの設定でクライアント証明書の検証用CA(認証局)だけを指定し、サーバー証明書を入れていない場合に起きる。この設定ではホスト名ごとのCAが反映されず、既定のCAで検証してしまう。そのため、requestCert と rejectUnauthorized を有効にしていても、既定のCAが署名した証明書を持つクライアントなら保護対象のホスト名へ接続できてしまう。
修正前と修正版を実機で比較した
手元の検証環境(macOS ARM64)で、v26.12.1とnodejs.orgから取得したv26.13.1を同じ条件で動かした(公式の SHASUMS256.txt でチェックサムを確認済み)。また、24.x系列の比較にはv24.22.2とv24.22.3を使った。サーバーはすべて 127.0.0.1 で検証している。
修正前で不具合の発生を確認でき、修正版で解消を確認できたのは以下の5件。
| CVE | v26.12.1 | v26.13.1 |
|---|---|---|
| CVE-2026-123865 | 切断処理中にプロトコルエラーを起こすとプロセスがハングし、強制終了になった(Node本体のリグレッションテストで再現) | 同じスクリプトが正常に終了した |
| CVE-2026-123877 | ca を省略したときと ca: [] を指定したときの Agent.getName() のキーが同じになり、既定のCAで認証したセッションを ca: [] 側でも再利用した | キーが分かれ、ca: [] の要求は通らなかった |
| CVE-2026-123867 | server.addContext('protected.test', { ca: CA2 }) のホスト名で、CA1が発行したクライアント証明書が通った | CA1の証明書は拒否し、CA2の証明書だけ通した |
| CVE-2026-123871 | http.setGlobalProxyFromEnv() に不正なURLを指定すると、エラーメッセージに http://proxy-user:sup3rS3cret@[::1 と認証情報がそのまま出た | http://[redacted]@[::1 に伏せ字になった |
| CVE-2026-123862 | 圧縮の送信中に deflate.params() を呼んでからストリームを破棄すると、コールバックが呼ばれず ERR_INTERNAL_ASSERTION: zlib binding closed がストリームの外で発生し、uncaughtException ハンドラーがないと終了コード1で異常終了した | 同じ操作でエラーがコールバックに戻り、終了コードは0だった |
一方、今回の手順や手元環境では差を確認できなかった項目は以下の6件。
| CVE | 手元での検証結果と状況 |
|---|---|
| CVE-2026-123875 | 公式のリグレッションテストを実行したが、修正前・修正版ともに正常終了しクラッシュは再現しなかった |
| CVE-2026-123868 | 本文を受信しないHTTP/2サーバーに1バイトの DATA フレームを流すと、フレーム1本あたり150〜260バイトがサーバー側に残り通信データ量の10〜20倍を保持した。ただし maxSessionMemory=2 MiB超過後に新規ストリーム20件を開いても双方とも通過し、上限の挙動は確認できなかった |
| CVE-2026-106071 | --allow-fs-read=<ディレクトリ>/passwd.* のワイルドカード指定で比較したが、一致・不一致の結果は双方同じで、必要以上に広く許可される動作にはならなかった |
| CVE-2026-123829 | --permission と --allow-fs-read=<監視ディレクトリ> の組み合わせでは、macOSで監視下のサブディレクトリの readdirSync まで許可された。指定で読み取り禁止サブディレクトリを作れず、ファイル名通知の挙動は双方同じだった |
| CVE-2026-123828 | リクエストの :authority をそのまま session.altsvc() に指定したところ、4000文字のホスト名では修正前・修正版ともに ERR_INVALID_URL となり、C++側のバッファ処理まで進まなかった |
| CVE-2026-123864 | res.flushHeaders() 後に本文を受け取りながら res.write(Buffer.alloc(0)) を呼ぶハンドラーで混入を確かめるコードを動かしたが、最後まで実行できずに打ち切った |
手元で差が出なかった項目も脆弱性が存在しないわけではないため、該当有無の判断はアドバイザリ記載の前提条件を参照してほしい。
アドバイザリとCHANGELOGで食い違っているところ
各ラインのCHANGELOG(22.23.5、24.22.3、26.13.1)を突き合わせると、アドバイザリと食い違う箇所がある。
| 項目 | アドバイザリ | CHANGELOG |
|---|---|---|
CVE-2026-123873(permission: check heap snapshot destination、LOW) | 載っていない | 3系列とも主な変更に載っている |
| CVE-2026-106071(ワイルドカードの許可) | 3系列が対象のLOWとして記載 | 主な変更に載っていなくて、src: fix the fs permission tree node on wildcard nodes というコミットだけがある。このコミットは22.23.5と24.22.3の一覧にあるが、26.13.1の一覧にはない |
| 22.xでのHTTP/2関連4件(CVE-2026-123863、123874、123828、123868) | 22.xは対象外 | 22.23.5の主な変更には載っているが、123863以外はコミット一覧に対応するコミットがない |
どちらが正確かは現時点の公開情報からは判断できないが、修正版へ更新すれば双方の修正が反映されるため、対応方針に違いはない。
依存ライブラリの更新
Node.js本体に組み込まれている依存ライブラリも更新された(プロジェクト配下の node_modules ではなく、Node.jsランタイムに内包されているライブラリ)。各バージョンはCHANGELOGの記載による。
| ライブラリ | 役割 | 22.23.5 | 24.22.3 | 26.13.1 |
|---|---|---|---|---|
| llhttp | HTTPのパーサー | 9.4.4 | 9.4.4 | 9.4.4 |
| undici | fetch の実装 | 更新なし | 7.30.2 | 8.12.4 |
| SQLite | node:sqlite | 3.54.1 | 3.54.1 | 3.54.1 |
| Brotli | 圧縮形式の実装 | 更新なし | 1.2.1 | 1.2.1 |
アドバイザリ冒頭では、公開済みの脆弱性に対応するための依存ライブラリ更新として、llhttp(22.x、24.x、26.x)、undici(24.x、26.x)、SQLite(22.x、24.x)が挙がっている。
ただしSQLiteは26.13.1のCHANGELOGにも deps: update sqlite to 3.54.1 が入っているため、上の表とアドバイザリ冒頭に記載された対象ラインは合致していない。
修正版への更新方法
修正版はv26.13.1、v24.22.3、v22.23.5が公開されている。
node -v で現在のバージョンを確認し、サポート終了済みのv25、v23、v20などを使っている場合は修正版が提供されないため、サポート期間内のv26、v24、v22系列へ切り替える。
リリーススケジュールでは、v26は10月28日にアクティブLTS(長期サポート版)になり、v24は10月20日からメンテナンスLTS、v22は2027年4月30日にサポートが終了する(Node.js Release schedule)。
2026年11月12日夜(JST)時点で、主な入手先での提供状況を確認した。
| 入れ方 | 更新の方法 | 11月12日夜時点の状況 |
|---|---|---|
| nodejs.org の配布物 | ダウンロードページ から入れ直す | 3系列とも公開済み |
| nvm(Node.jsの版を切り替えるツール) | nvm install 24 で24.xの最新を入れる | 公式配布物から取得可能 |
| Docker | FROM node:24.22.3 のように版を指定し、イメージを作り直す | Docker Hubに node:22.23.5、node:24.22.3、node:26.13.1 のタグを確認 |
| Homebrew | brew upgrade node(node@24、node@22 も同様) | node 26.13.1、node@24 24.22.3、node@22 22.23.5 に更新済み |
Dockerで node:24 のような版を固定しないタグを使っていても、キャッシュに残った古いベースイメージからビルドすると修正前のバージョンのままになるため、docker pull で最新イメージを取得してから再ビルドする。