Node.jsの10月21日セキュリティリリース:Brotliの欠陥(Node2Shell)を含む10件のCVEと、更新の手順

Node.jsの10月21日セキュリティリリース:Brotliの欠陥(Node2Shell)を含む10件のCVEと、更新の手順

2026年10月21日、PHPの重大な欠陥「PHP2Shell」の修正版が出たのと同じ日に、JavaScriptをサーバーやパソコンの上で動かすための実行環境「Node.js(ノード・ジェイエス)」も、セキュリティの修正をまとめた新しい版を公開しました。対象は、いま公式に保守されている3つの系統すべてで、修正された欠陥は10件、そのうち4件が4段階の上から2番目にあたる「High(高)」と評価されています。

なかでも注目を集めたのが、データを小さくする圧縮形式「Brotli(ブロトリ)」を元に戻す処理に見つかった欠陥 CVE-2026-111497 です。細工された圧縮データを展開させると、Node.jsのプログラムの記憶領域(メモリ)が壊され、条件によっては攻撃者の用意した命令を実行されるおそれがあります。翌22日にはセキュリティ会社Impervaが、公式に配られているNode.jsのコンテナイメージで実際に命令の実行まで到達したと公表し、「Node2Shell」という通称が広まりました。

この記事では、10月21日のNode.jsのセキュリティリリースで直された10件の欠陥を、公式の発表とCVEの記録をもとに一つずつ噛みくだいて説明します。そのうえで、自分のアプリが影響を受けるのかを見分ける方法、nvmやVolta、Docker、CIでの具体的な更新手順、そしてサポートの終わった古い版を使っている場合にどうするかを、立場ごとにまとめます。

先に結論を書いておくと、次の5つです。

この記事の要点
  • 修正版は 22.23.4、24.22.1、26.12.1 の3つです。22系・24系・26系を使っている人は、このいずれか以降に上げれば10件すべての修正が入ります。Node.js 20以前はすでにサポートが終わっており、修正版は出ません。
  • いちばん重いのは Brotli の展開処理の欠陥 CVE-2026-111497 です。Node.jsの公式の評価は「High」ですが、CVEの記録ではCVSS 4.0で9.2(Critical、緊急)とされており、評価が分かれています。通称も「Node2Shell」「HTTP2Shell」「Broccoli2Shell」など資料によって揺れています。
  • 危ないのは、外のサーバーから受け取った応答を fetch() で読むアプリや、Express 5・body-parser 2 などで圧縮されたリクエストを自動で展開しているアプリです。Node.jsの素のHTTPサーバーは、Brotliのデータを受け取っただけでは展開しません。
  • 10月24日の時点で、実際の攻撃に使われたという報告はありません。ただし、Impervaが公式のNode.js 24.22.0のイメージで命令の実行を再現しており、「理屈の上だけの欠陥」ではなくなっています。
  • 同じリリースで、同梱のルート証明書がNSS 3.131.1に更新され、秘密鍵が漏れたとされるeMudhra(イーマドラ)の中間証明書を拒否する仕組みも入りました。Brotliを使っていない人にとっても、今回の更新を見送る理由はありません。

10月21日のリリースで何が出たのか

データセンターのサーバーラックとカートの上のノートPC

Node.jsは、もともとWebブラウザの中で動く言語だったJavaScriptを、サーバーや自分のパソコンの上で動かせるようにした実行環境です。Webアプリのサーバー、APIの裏側、フロントエンドの開発道具(ビルドツールやテストツール)など、プログラムを書く人の身の回りの多くのところで使われています。

「自分はNode.jsでサーバーを書いていない」という人でも、Viteやwebpack、ESLint、VS Codeの拡張機能などを通じて、毎日Node.jsを動かしていることが少なくありません。

Node.jsには、偶数の番号の系統が長期サポート(LTS、Long Term Support)になり、何年か修正が続くという決まりがあります。今回修正版が出たのは、次の3つの系統です。公式のリリースノートの見出しでは、22系と24系が「LTS」、26系が「Current(最新の系統)」と書かれています。

系統修正版位置づけ(リリースノートの表記)この系統で直ったCVEの数
26系26.12.1Current10件
24系24.22.1LTS(愛称 Krypton)8件
22系22.23.4LTS7件

表の右端の数が系統によって違うのは、欠陥のいくつかが新しい系統にだけある機能で起きていたためです。たとえば ZIP ファイルを読む機能の欠陥は26系だけ、TLSの欠陥の1件は24系と26系だけに影響します。逆に言えば、どの系統を使っていても、その系統の最新の修正版に上げれば、自分に関係する分はすべて直ります。

公式の発表は、Node.jsのブログの「October 2026 Security Releases」という記事です。そこでは、最も高い深刻度が「HIGH」であること、影響を受ける系統が26・24・22であることに加え、依存している部品の更新として、名前解決(ドメイン名からIPアドレスを調べること)を担う c-ares、暗号の処理を担う OpenSSL、組み込みのデータベース SQLite、HTTPの通信を担う undici、そして「eMudhra中間CAの侵害」に対応するルート証明書の更新が挙げられています。

10件の一覧を、先に表にしておきます。深刻度はNode.jsの公式の評価で、CVSSの点数は、CVEの記録を管理する団体(CNA)が付けたものです。

Brotliの1件はGoogleが、それ以外の9件はHackerOne(企業が欠陥の報告を受け付ける仕組みの大手で、Node.jsの報告窓口にもなっています)が記録を作っています。CVSSは、欠陥の深刻さを0から10までの点数で表す共通の物差しです。

CVE公式の深刻度CVSS内容(ひと言で)影響する系統
CVE-2026-111497High9.2(v4.0)Brotliの展開処理で境界外への書き込み。命令の実行のおそれ26・24・22
CVE-2026-105483High7.4(v3.0)全角数字で書いたIPアドレスでTLSのホスト名確認をすり抜け26・24
CVE-2026-106069High7.4(v3.0)先頭に0を付けたIPアドレスの読み方の違いでTLSのホスト名確認をすり抜け26・24・22
CVE-2026-105482High7.4(v3.0)dns.resolve系がNUL文字を含む名前を途中で切ってしまう26・24・22
CVE-2026-105481Medium6.5(v3.0)使い回しの接続で、送る前に届いていた応答を取り違える26・24・22
CVE-2026-106072Medium6.7(v3.0)変わった書き方のIPv4で、DNS名の証明書がIPの相手として通ってしまう26・24・22
CVE-2026-106074Medium4.8(v3.0)スナップショットや単一実行ファイルで乱数の作り置きが共有される26・24・22
CVE-2026-106070Low3.7(v3.0)ZIPの見出しの検査に失敗したあと、攻撃者の選んだ中身を返す26
CVE-2026-106071Low3.6(v3.0)権限モデルのワイルドカード指定で、許可より広く読めてしまう26
CVE-2026-106073Low3.3(v3.0)権限モデルで fs.realpathSync の確認が抜けていた26・24・22

CVEの記録が公開された日は、BrotliのCVE-2026-111497が10月22日、残りの9件が10月23日でした。修正版の公開(21日)から番号の正式な記録まで少し間があったため、21日から22日にかけての報道では、番号が書かれていなかったり、公式と違う番号が紹介されたりした例もあります。対応するときは、Node.jsの公式ブログの番号を基準にしてください。

図:公開から10月24日までの時系列
  1. 10月21日修正版を公開
  2. 10月22日BrotliのCVE-2026-111497の記録を公開。Impervaが命令の実行まで到達したと公表
  3. 10月23日残りの9件のCVEの記録を公開
  4. 10月24日実際の攻撃に使われたという報告はない(この時点)
ポイント

なお、今回の10件のうち3件は、以前のリリースで直したはずの欠陥の「直し残し」だと公式に書かれています。dns.resolve の件(以前は dns.lookup だけを直していた)、使い回しの接続の件、fs.realpathSync の件(以前は fs.realpathSync.native だけを直していた)です。TLSの全角数字の件は、以前の修正のあとに入ってしまった「先祖返り(リグレッション)」とされています。

同じ場所に何度も手が入っているということは、それだけ攻撃の糸口として調べられている場所だということでもあります。

いちばん重いCVE-2026-111497:Brotliの展開処理で何が起きるのか

サーバーの基板とメモリモジュールの接写

まず、Brotliについて説明しておきます。Brotliは、Googleが作った圧縮の形式で、Webページやスクリプト、画像以外のファイルを小さくして、通信の量を減らすために使われます。

昔からある gzip よりもよく縮むため、多くのWebサーバーやCDN(世界中に置かれた配信用のサーバー群)が対応しており、HTTPの通信では「Content-Encoding: br」という印が付いたデータとして流れています。Node.jsは、このBrotliを展開するためのGoogle製のC言語のプログラムを、本体の中に組み込んで配っています。

欠陥の中身

Node.jsの公式の説明によると、問題は「不正なブロックの切り替え」と呼ばれる壊れたデータを受け取ったときの扱いでした。本来なら「このデータは形式として間違っている」と判断して処理をやめるべきところを、展開の処理は「入力がまだ足りないだけだ」と勘違いしていました。

そのため、続きのデータを待ちながら、内部にある8バイトしかない小さな作業用の領域に、入力を書き足し続けてしまいます。決められた大きさを超えて書き込むので、その先にある別のデータを上書きしてしまうわけです。

図:Brotliの欠陥が起きる流れ
STEP 1壊れたデータ「不正なブロックの切り替え」を含む圧縮データ
STEP 2誤った判断「入力がまだ足りないだけ」と勘違い
STEP 3書き込みが続く8バイトの作業用の領域に書き足し続ける
STEP 4境界外書き込み異常終了・メモリの漏洩・命令の実行のおそれ
Node.jsの公式の説明をもとにした流れです

このように、確保した場所の外側にデータを書き込んでしまう欠陥を「境界外書き込み(out-of-bounds write)」と呼びます。C言語のように、メモリの管理をプログラマーが自分でする言語で起きやすい種類の欠陥です。

上書きされる場所に何があるかによって、プログラムが落ちるだけで済むこともあれば、記憶領域の中身が外に漏れることも、攻撃者が用意した命令を実行されることもあります。公式は、node:zlib で展開されるBrotliのデータによって、プロセスの異常終了、メモリの内容の漏洩、またはリモートコード実行(RCE、離れた場所から命令を実行されること)が起きうると説明しています。

CVEの記録では、弱点の種類として「CWE-787 境界外書き込み」と「CWE-252 戻り値の確認漏れ」の2つが挙げられています。後者は、「処理が失敗した」という知らせを受け取ったのに、それを確かめずに先へ進んでしまう種類の欠陥のことです。今回の修正では、壊れたデータを受け取ったときに、展開の処理がきちんと「形式の誤り」を返すように変わりました。Node.js側でも、この新しい誤りの種類を表す定数 BROTLI_DECODER_ERROR_FORMAT_BLOCK_SWITCH を外から見えるようにする変更が入っています。

欠陥はNode.jsではなくBrotli本体にあった

ここで大切なのは、欠陥がNode.jsが自分で書いたプログラムではなく、組み込んでいるGoogleのBrotliの側にあったという点です。CVEの記録を作ったのはGoogleで、影響を受けるのはBrotliの 1.0.3 から 1.0.10 より前、1.1.0 から 1.1.1 より前、1.2.0 から 1.2.1 より前とされています。つまり、Brotli 1.0.10、1.1.1、1.2.1 が修正版です。

Brotliを組み込んでいるソフトはNode.jsだけではありません。Pythonの「Brotli」や「brotlicffi」というパッケージも同じ欠陥の影響を受け、GitHubのセキュリティ情報(GHSA-9v8h-qqgm-4m35)では、Brotli 1.2.1 と brotlicffi 1.2.1.0 が修正版として挙げられています。

PyPI(Python用の公式の配布所)では、Brotli 1.2.1 が10月22日に公開されました。報道では、JavaScriptの別の実行環境であるBunも1.4.4で修正したと伝えられています。Pythonの側の話は、関連記事のPythonの回で扱う予定なので、ここでは「Node.jsだけの問題ではない」という点だけ押さえておいてください。

ポイント

報道によると、欠陥の元になった部分の修正そのものは、2026年2月にはBrotliのソースコードに入っていました。ところが、修正を含む正式な版がしばらく出なかったため、Brotliを組み込んで配っているソフトには古いままの展開処理が入り続けていました。

ソースコードの上で直っていても、それが正式な版として出て、さらに各ソフトに取り込まれて配られるまでは、利用者の手元は直らないということです。これは、部品を組み込んで配るソフトに共通する弱さでもあります。

深刻度は「High」か「9.2」か

この欠陥の深刻度は、資料によって違って見えます。Node.jsの公式の発表では、4段階の上から2番目の「High」です。一方、Googleが作ったCVEの記録では、CVSS 4.0という新しい物差しで9.2点の「Critical(緊急)」とされ、GitHubのセキュリティ情報も同じ点数を載せています。

「CVSS 9.2」と書いている記事と「High」と書いている記事は、どちらも間違いではなく、評価した組織と物差しが違うということです。

図:同じ欠陥の評価の違い
Node.jsの公式の発表
  • 4段階の上から2番目の「High」
CVEの記録(Google)
  • CVSS 4.0で9.2点の「Critical(緊急)」
  • GitHubのセキュリティ情報も同じ点数

CVSSの採点の中身を見ると、ネットワーク越しに攻撃でき、ログインも利用者の操作もいらず、成功すれば情報の秘密・正しさ・使えることのすべてに大きな影響がある、という評価になっています。ただし「AT:P」という項目があり、これは「攻撃が成り立つには、攻撃者が自由にできない前提条件がある」という意味です。

今回でいえば、アプリが攻撃者の用意したBrotliのデータを展開する作りになっていることが、その前提にあたると考えられます。Node.jsがHighにとどめているのも、すべてのNode.jsのアプリが自動的に危ないわけではないことを反映しているとみられます。

米国のサイバーセキュリティ・インフラストラクチャ安全保障庁(CISA)は、CVEの記録に追加の評価を付けています。10月23日の時点で、悪用は「none(なし)」、攻撃の自動化のしやすさは「no」、成功したときの影響は「total(全面的)」とされています。CISAが実際に悪用が確認された欠陥を集めている一覧(KEV)にも、この欠陥は入っていません。

通称が揺れている理由

この欠陥には、資料によっていくつもの通称が付いています。Node.jsの公式の発表には通称はなく、番号と「Brotli decoder out-of-bounds write permits remote code execution(Brotliの展開処理の境界外書き込みによりリモートコード実行が可能)」という題名だけが書かれています。

通称主な出どころ名前の意味
Node2ShellImpervaの研究者Ron Masas氏の解説(10月22日)Node.jsからシェル(命令を実行できる状態)へ。PHP2Shellにならった名前
HTTP2Shell公表の数日後にSNSで広まり、一部のセキュリティ企業のブログも採用HTTPで届くデータからシェルへ
Broccoli2Shell欠陥のまとめサイト broccoli2shell.comBrotliにかけた名前。Node.js以外にも影響するため、Node.jsの名を外した
Python2Shell一部の資料Pythonのパッケージにも影響することから

通称が増えたのは、同じ日に出たPHP2Shellの印象が強く、「〇〇2Shell」という呼び方が分かりやすかったためでしょう。ただ、名前が違うと「別の欠陥なのか」と迷う原因になります。社内で連絡するときや、ほかの人に確認を頼むときは、通称ではなく CVE-2026-111497 という番号で伝えるのが確実です。

報告者についても、資料によって書き方が違います。Node.jsの公式の発表の表では、報告者は「OpenAI Codex Security(Khai Tran氏とSylvie Mayer氏)」、修正の担当はMatteo Collina氏とAntoine du Hamel氏とされています。一方で、Impervaの解説などでは、Matteo Collina氏を報告者として紹介しています。

この記事では公式の発表の表に従います。OpenAIのAIを使ったセキュリティの仕組みが見つけた欠陥だという点は、関連記事の「AIが脆弱性を見つける時代」で書いた流れと重なります。

自分のアプリは危ないのか:Brotliを展開する場面を見分ける

薄暗いサーバー室のネットワークスイッチとケーブル

CVE-2026-111497が怖いのは、攻撃に使うデータが「ただの圧縮データ」だという点です。ふだんの通信にも普通に流れている形式なので、見た目で怪しいと判断しにくく、アプリの奥のほうで自動的に展開されることも多いからです。逆に言えば、自分のアプリが「どこで、誰が用意したBrotliのデータを展開しているか」が分かれば、危なさの程度もかなり見えてきます。

公式の調査で確かめられた2つの道筋

Impervaは10月22日の解説で、公式に配られているNode.js 24.22.0のLinux(arm64)のコンテナイメージを使い、新しく立ち上げたコンテナで、攻撃者の用意した命令が実行されるところまで再現したと報告しました。同じ入力は、修正版の24.22.1では拒否されたとしています。攻撃のデータが届く道筋として挙げられたのは、次の2つです。

  • Node.jsに組み込まれている fetch() で、外のサーバーから圧縮された応答を受け取る場合。fetch() は、応答に「Content-Encoding: br」の印が付いていると、プログラムが何もしなくても自動で展開します。
  • Express 5.0.0〜5.3.0、または body-parser 2.0.0〜2.3.0 の仕組みで、圧縮されたリクエストの本文(POSTで送られてくるデータなど)を自動で展開している場合。body-parser は、Expressでリクエストの本文を読み取るための部品で、JSONなどを受け取るときにBrotliの圧縮も自動で解く機能を持っています。
注意

ここで注意したいのは、Express や body-parser そのものに欠陥があるわけではないことです。これらはBrotliのデータをNode.jsの展開処理に渡す「通り道」にすぎません。Impervaも、これらの版は欠陥のある処理に届く道筋を示したものだと書いています。

そのため、Express や body-parser を新しくしても、この欠陥は直りません。直すには、Node.jsそのものを修正版に上げる必要があります。

また、Impervaは、命令の実行は毎回成功したわけではなく、自分たちの環境では何度か試す必要があったと書いています。結果は、試したLinuxのarm64の版とアプリの設定に限ったものだとも断っています。攻撃の具体的なやり方は公開されていません。

当てはまるかどうかの目安

Impervaの解説や、欠陥のまとめサイトの説明をもとに、どんなアプリが影響を受けやすいかを表にまとめます。「危なさ」は、攻撃者が用意したBrotliのデータが展開の処理に届きやすいかどうかの目安です。

使い方危なさの目安理由
利用者が指定したURLを fetch() で取りに行く高い相手のサーバーを攻撃者が用意できる。OGP画像の取得、Webhookの送信先、リンクのプレビュー、スクレイピングなど
Express 5・body-parser 2 で圧縮された本文を受け付ける高い誰でもリクエストを送れる公開APIなら、攻撃者が直接データを送り込める
自分のコードで zlib.brotliDecompress などを呼ぶデータの出どころ次第アップロードされたファイル、キャッシュ、メッセージキューの中身など、外から来るデータを展開しているなら高い
信頼できる決まった相手のAPIだけを fetch() で呼ぶ低め相手のサーバーが乗っ取られない限り、細工されたデータは届きにくい
node:http の素のサーバーで本文を自分で読むだけ低い素のサーバーは、Brotliの本文を受け取っただけでは展開しない
手元でビルドツールやテストを動かすだけ低い外から細工されたデータが入る経路が少ない。ただし更新はしておく

表の一番上の「利用者が指定したURLを取りに行く」作りは、意外とあちこちにあります。たとえば、SNSのようにURLを貼るとページの題名や画像を表示する機能、利用者が登録したURLに通知を送るWebhookの機能、記事を取り込むRSSリーダー、AIのエージェントがWebのページを読みに行く機能などです。

こうした機能は、もともと「SSRF(サーバーに意図しない場所へアクセスさせる攻撃)」の対策として行き先を絞っていることがありますが、今回は行き先が普通のインターネット上のサーバーでも、そのサーバーが細工した応答を返せば攻撃が成り立ちます。行き先の制限だけでは防げない点に注意してください。

Fastify・Next.jsなど、ほかの仕組みの場合

Express以外の仕組みについては、10月24日の時点で、Impervaのような具体的な検証の公表は見当たりませんでした。ただ、仕組みとして当てはまりうるものはあります。

たとえば、Fastify(ファスティファイ、Expressと並んでよく使われるWebの仕組み)の公式の部品「@fastify/compress」には、圧縮されたリクエストの本文を自動で展開する機能があり、対応する形式に br(Brotli)が含まれています。説明書きによると、この展開は初期設定で有効で、受け付ける形式は requestEncodings という設定で絞れます。

Next.jsやNuxt、Remixなどの、画面とサーバーをまとめて作る仕組みについても、「Brotliのデータをどこで展開しているか」で考えるのが基本です。サーバー側の処理の中で外部のAPIを fetch() で呼んでいれば、その応答は自動で展開されます。

axiosやgotなど、fetch() 以外のHTTPの部品を使っている場合も、多くは圧縮された応答を自動で展開する作りになっています。どの部品を使っていても、展開の処理はNode.jsの中のBrotliが担うので、結局はNode.js本体を修正版に上げるのがいちばん確実です。

ポイント

なお、VercelのようなNode.jsのアプリを置くクラウドサービスでは、Node.jsの細かい版をサービス側が自動で更新する仕組みになっていることがあります。Vercelの説明書きでは、セキュリティの修正などのために、小さな版や修正版を自動で展開することがあると書かれています。

ただ、一度ビルドしたものが古い版のまま動き続ける場合もあるので、使っているサービスの案内を確かめ、必要なら作り直し(再デプロイ)をしてください。報道では、VercelがNode.js 20を使っているプロジェクトに22か24への切り替えと再デプロイを勧めているとも伝えられています。

PoCと悪用の状況を分けて整理する

PoC(Proof of Concept、欠陥が本当に突けることを示す試作の攻撃コード)と、実際の悪用は分けて考える必要があります。10月24日の時点で分かっていることを整理すると、次のとおりです。

  • Impervaは、公式のNode.js 24.22.0のイメージで命令の実行を再現したと公表しました。ただし、そのやり方は公開していません。
  • 報道によると、GitHubには別の人が作ったPoCが公開されています。こちらは、わざと守りを弱めたテスト用のNode.jsでは命令の実行に届いたものの、普通に配られている版では、プロセスを落とすことと、記憶領域の一部を読むことまでしかできなかったとされています。
  • Pythonの側では、研究者がメモリの誤りを見つける道具(AddressSanitizer)で欠陥を確かめたものの、攻撃までは試していないと報じられています。
  • Node.jsのチームやセキュリティ専門メディアの報道では、実際の攻撃に使われた例は報告されていません。CISAの評価も「悪用なし」です。

つまり、「試作の攻撃が成功したことは確認されたが、実際に攻撃された例は確認されていない」という段階です。ただ、PHP2Shellのときもそうだったように、詳しい解説が出ると、攻撃者が同じ道筋をたどろうとするまでの時間は短くなります。

セキュリティ会社のなかには、今後数週間で攻撃の道具やインターネット全体を調べる動き(スキャン)が増えると見ているところもあります。悪用の報告がないうちに更新を済ませておくのが、いちばん手間が少なく済みます。

Highのほか3件:TLSのホスト名確認とDNSの名前の切り詰め

データセンターの光ファイバーのパッチパネル

Brotli以外のHighの3件は、どれも「通信の相手を正しく確かめる」仕組みに関わる欠陥です。命令を実行されるような派手さはありませんが、本物のサーバーだと思って通信していた相手が、実は別の相手だった、ということが起こりえます。CVSSはいずれも7.4で、条件がそろうのが難しい(攻撃の難しさが高い)と評価されています。

前提:TLSのホスト名確認とは

HTTPSなどの暗号化された通信では、TLS(ティーエルエス)という仕組みが使われています。TLSでは、接続先のサーバーが「証明書」を見せ、クライアント(接続する側)はその証明書が信頼できる認証局から発行されたものか、そして「今つなごうとしている相手の名前」に対して発行されたものかを確かめます。後者の確認を「ホスト名の検証」と呼び、Node.jsでは tls.checkServerIdentity() という関数がこれを担っています。

証明書には、どの名前に対して有効かが書かれています。この欄を SAN(Subject Alternative Name、サブジェクト代替名)と呼び、「DNS:example.com」のようなドメイン名の書き方と、「IP:192.0.2.1」のようなIPアドレスの書き方があります。

正しくは、IPアドレスでつないだときはIPアドレスの欄と、ドメイン名でつないだときはドメイン名の欄と照らし合わせなければなりません。今回の3件のうち2件と、Mediumの1件は、この「IPアドレスかどうか」の判断の食い違いを突くものです。

CVE-2026-105483:全角数字のIPアドレス

1件目は、「127.0.0.1」のように、IPアドレスの一部を全角の数字で書いた場合の欠陥です。全角の数字は、正規化(文字の表記をそろえる処理)をすると普通の「127.0.0.1」になりますが、tls.checkServerIdentity() はこれをIPアドレスだと認識せず、ドメイン名として扱っていました。そのため、「DNS:127.0.0.1」や「DNS:*.0.0.1」といった、ドメイン名の欄にIPアドレスのような文字を入れた証明書が、本来発行されていないIPアドレスの相手として受け入れられてしまうおそれがありました。

この件は、以前のCVE-2026-48618の修正のあとに生じた「先祖返り」だとされており、影響は24系と26系だけです。22系は対象外です。

CVE-2026-106069:先頭に0を付けたIPアドレス

2件目は、「0127.0.0.1」のように、数字の先頭に0を付けた書き方の欠陥です。IPアドレスの書き方には古くからの決まりがいくつかあり、先頭に0を付けると8進数(8で繰り上がる数え方)とみなす流儀があります。Node.jsのホスト名の確認は、Webの標準(WHATWGのURLの決まり)にならって「0127」を8進数として読み、「87.0.0.1」と解釈していました。

ところが、macOSなど一部の環境では、実際に接続先を決めるOSの名前解決の仕組みが「0127」を10進数の「127」と読みます。つまり、ホスト名の確認では「87.0.0.1」の証明書として扱い、実際の接続は「127.0.0.1」に向かう、というずれが生まれます。その結果、接続した先のサーバーが、別のIPアドレスに対して発行された証明書で本物になりすませてしまうおそれがありました。

CVE-2026-105482:dns.resolveの名前の切り詰め

3件目はDNS(ドメイン名からIPアドレスなどを調べる仕組み)の欠陥です。Node.jsの dns.resolve で始まる関数群(dns.resolve4 や dns.resolveTxt など)と dns.reverse は、名前の途中にNUL文字(値が0の、目に見えない特別な文字)が入っていると、そこで名前を黙って切り捨てていました。

公式の説明の例でいうと、「attacker.example\0.trusted.example」という名前を調べさせると、エラーにならずに「attacker.example」の情報が返ってきます。C言語の世界では、NUL文字は「文字列の終わり」を意味するため、名前解決の部品 c-ares に渡った時点で、その先が無視されてしまうのです。アプリが「名前の末尾が trusted.example なら信頼する」といった確認をしてから dns.resolve で調べる作りだと、確認したのとは別の名前の情報で処理を進めてしまうことになります。

この件も、以前のCVE-2026-48930の修正の直し残しです。以前の修正では dns.lookup と、net・tls の接続先の指定だけが直されており、dns.resolve 系は残っていました。CVEの記録では、弱点の種類は「不適切なアクセス制御」とされています。

この3件で困るのはどんなアプリか

この3件で実際に困るのは、利用者や外部から受け取った「接続先の名前」をそのまま使って通信するアプリです。たとえば、利用者が設定した接続先にHTTPSで通知を送る機能や、社内の別のサービスにIPアドレスで直接つないでいる仕組み、名前の末尾で信頼できる相手かを判断する仕組みなどです。接続先を自分のプログラムの中で決め打ちしているだけなら、攻撃者が変わった書き方の名前を入れ込む余地はほとんどありません。

それでも、TLSの確認は「最後の砦」のような役割を持っています。ほかの守りがすり抜けられたときに、なりすましを止めるための仕組みだからです。自分のアプリが当てはまらないと思っても、更新しておく価値は十分にあります。

あわせて、外から受け取ったホスト名やIPアドレスは、決まった書き方(全角を含まない、先頭に0のない10進数のIPv4など)になっているかを自分のプログラムでも確かめておくと、同じ種類の欠陥が別の場所に出てきたときにも被害を受けにくくなります。

MediumとLowの6件:使い回しの接続、乱数、ZIP、権限モデル

夜の机に置いた観葉植物とマグカップ

残りの6件は、深刻度がMediumかLowの欠陥です。多くは特定の機能を使っている場合にだけ関係しますが、自分が当てはまるかどうかを判断するために、一つずつ中身を見ておきます。

CVE-2026-105481(Medium):使い回しの接続で応答を取り違える

HTTPでは、同じサーバーと何度もやりとりするときに、接続を切らずに使い回す「キープアライブ」という仕組みがあります。Node.jsのHTTPクライアントは、使っていない間の接続に、頼んでもいないデータが届いたら、その接続を捨てる見張りの仕組みを持っています。ところが、この見張りは、Node.jsが入出力の様子を確かめる(イベントループが入出力をポーリングする)タイミングでしか働いていませんでした。

そのため、たとえば重い計算などの同期処理をしている間に、頼んでいない応答が届き、まだ読まれていない状態でその接続が次のリクエストに使い回されると、見張りが働かず、先に届いていたデータが「新しいリクエストへの応答」として読まれてしまいます。悪意のある、または乗っ取られたサーバーが、偽の応答を先回りして送り込める余地があったということです。

CVEの記録では、確認したときと使ったときの間に状態が変わる「TOCTOU(タイム・オブ・チェック、タイム・オブ・ユース)」の種類の欠陥とされ、CVSSは6.5です。これもCVE-2026-48931の直し残しとされています。

CVE-2026-106072(Medium):変わった書き方のIPv4とDNS名の証明書

前の節のTLSの話の続きです。証明書にドメイン名の欄(DNSのSAN)しかない場合でも、クライアントが「0177.0.0.1」「0x7f.0.0.1」「2130706433」「127.1」のような、普通とは違う書き方のIPv4アドレスで接続すると、そのドメイン名の証明書がIPアドレスの相手の証明として通ってしまうおそれがありました。どれも、書き方によっては「127.0.0.1」を表す表記です。

攻撃が成り立つには、信頼された認証局から、数字だけのドメイン名の証明書が発行されていることと、glibc(Linuxで広く使われる基本の部品)のように、こうした書き方を受け付ける名前解決の仕組みが使われていることが必要です。条件は厳しめですが、24系と22系のリリースノートでは、CVE-2026-106069とまとめて「普通ではない書き方のIPアドレスを拒否する」修正として入っています。CVSSは6.7です。

CVE-2026-106074(Medium):スナップショットに乱数の作り置きが入る

Node.jsには、次の2つの機能があります。

  • 起動を速くするために、起動したあとの状態をまるごと保存しておく「スタートアップスナップショット」(–build-snapshot というオプションで作ります)
  • アプリとNode.jsをひとつの実行ファイルにまとめる「単一実行ファイル(SEA、Single Executable Applications)」。SEAでも、useSnapshot という設定でスナップショットを使えます。

crypto.randomUUID()、crypto.randomUUIDv7()、crypto.randomInt() は、速さのために、乱数をあらかじめまとめて作り置きしておく仕組みを持っています。今回の欠陥は、この作り置きがスナップショットの中に保存されてしまうというものでした。

その結果、同じスナップショットから起動したすべてのプロセスが、作り置きが入れ替わるまでの間、同じ値を返します。公式の説明では、最大127個のUUIDと、約1,000個の randomInt() の値が重なりうるとされています。

UUIDは、データベースの行や注文、セッションなどを区別するための、重なることのない前提の識別子です。複数のプロセスで同じUUIDが出れば、データがぶつかって上書きされるおそれがありますし、値を予想されれば、推測されては困る識別子として使っている場合に危険です。

スナップショットやSEAを使っていない普通のアプリには関係しません。CVSSは4.8です。

注意

この欠陥で気をつけたいのは、Node.jsを更新しただけでは直らない場合があることです。古い版で作ったスナップショットやSEAの実行ファイルには、問題のある状態がすでに焼き込まれています。

セキュリティ専門メディアの解説でも、単一実行ファイルやスナップショットとして配っているアプリは作り直すよう勧められています。修正版のNode.jsで、もう一度作り直してから配り直してください。

CVE-2026-106070(Low):ZIPの見出しの検査に失敗したあとの取り違え

26系には、ZIPファイルを読む ZipFile という新しい機能があります。ZIPファイルの中には、それぞれのファイルの手前に置かれる「ローカルヘッダー」と、末尾にまとめて置かれる「セントラルディレクトリ」という、2つの見出しの情報があります。今回の欠陥は、この2つが食い違うZIPファイルで zipEntry.modified(更新日時)を読むと、検査のエラーは捕まえられるものの、中途半端に読んだ見出しが記憶されたまま残るというものでした。

その後に中身を読むと、ファイルの先頭からの、本来とは違うデータが返ってきます。ZIPには中身が壊れていないかを確かめるCRC-32という検査の値がありますが、セントラルディレクトリの値も攻撃者が決められるので、この検査も通ってしまいます。

影響は26系だけで、CVSSは3.7です。26系でZipFileを使って、外から受け取ったZIPファイルを読んでいる場合に関係します。

CVE-2026-106071(Low):権限モデルのワイルドカードで許可が広がる

Node.jsには「権限モデル(Permission Model)」という、プログラムが読み書きできるファイルやできることを絞る仕組みがあります。–permission というオプションで有効にし(古い版では –experimental-permission という名前でした)、–allow-fs-read などで読んでよい場所を指定します。信用しきれないプログラムを動かすときに、被害の範囲を狭める目的で使われます。

今回の欠陥は、ワイルドカード(* を使った「〜で始まるもの全部」という指定)で許可を出したとき、その指定よりも短い名前のファイルまで許可されてしまうというものです。公式の例では、–allow-fs-read=/etc/passwd.* と指定すると、/etc/passwd.bak などだけでなく、/etc/passwd そのものも読めてしまいました。ワイルドカードの指定が、許可の木構造のいちばん上の直下にある場合にだけ起きるとされています。

影響は26系だけで、CVSSは3.6です。以前のCVE-2026-58043とは原因が別だとも書かれています。

CVE-2026-106073(Low):fs.realpathSyncの確認漏れ

権限モデルの、もう1件の欠陥です。fs.realpathSync() は、シンボリックリンク(別の場所を指す、ショートカットのようなファイル)をたどって、ファイルの本当の場所を調べる関数です。この関数に、読み取りの許可を確かめる処理が入っていませんでした。そのため、–permission で読める場所を絞っていても、許可していない場所にファイルがあるかどうかを調べたり、リンクの行き先を知ったり、場所の一覧を探ったりできてしまいました。

ファイルの中身を読めるわけではありませんが、どこに何があるかが分かるだけでも、次の攻撃の手がかりになります。これはCVE-2026-21715の直し残しで、以前は fs.realpathSync.native() だけが直されていました。CVSSは3.3です。

権限モデルについては、2026年に入ってから何度もすり抜けの欠陥が見つかっています。Node.jsの公式の説明書きでも、権限モデルは悪意のあるコードを完全に閉じ込めるための仕組み(サンドボックス)ではないという趣旨の注意書きがあります。信用できないコードを動かす場合は、権限モデルだけに頼らず、コンテナや仮想マシン、OSの利用者の分離などと組み合わせるのが安全です。

同梱のルート証明書とeMudhra:Brotliを使っていなくても更新する理由

ラックに並んだネットワーク機器と点滅するランプ

今回のリリースには、CVEの番号が付いていない、もうひとつ大切な変更が入っています。Node.jsに同梱されている「ルート証明書」の更新と、秘密鍵が漏れたとされる中間証明書を拒否する仕組みの追加です。Brotliを使っていないアプリでも、HTTPSで外部と通信しているなら、この変更は関係します。

ルート証明書と中間証明書のおさらい

先ほどのTLSの説明で、サーバーの証明書は「信頼できる認証局から発行されたもの」かどうかを確かめると書きました。この「信頼できる認証局」の一覧の大元にあたるのがルート証明書です。

実際のサーバーの証明書は、ルート証明書から直接ではなく、その下にある「中間証明書」を通じて発行されるのが普通です。ルートから中間、中間からサーバー、という信頼の鎖がつながっていれば、その証明書は信頼できると判断されます。

図:証明書のつながり
STEP 1ルート証明書信頼する認証局の一覧の大元
STEP 2中間証明書ルートの下から発行される
STEP 3サーバーの証明書接続先のサーバーが見せる
信頼の鎖がつながっていれば、その証明書は信頼できると判断されます

Node.jsは、Firefoxを作っているMozillaが管理するルート証明書の一覧(NSSという暗号の部品に含まれています)を、本体の中に組み込んで配っています。そのため、OSの証明書の一覧を更新しても、Node.jsは基本的に自分が持っている一覧を使い続けます。Node.jsの同梱の一覧を新しくするには、Node.jsそのものを更新する必要があります。

eMudhraの件とNode.jsの対応

10月中旬、インドの認証局eMudhraの中間証明書の秘密鍵が、公開されている情報から計算できてしまう形で作られていたと報告されました。秘密鍵が分かれば、その中間証明書の名前で、任意のサイトの証明書を作れてしまうおそれがあります。

ChromeやFirefoxなどのブラウザは、報告から短い時間のうちに問題の証明書を遮断しました。事件そのものの経緯は、関連記事のeMudhraの回で詳しく扱う予定なので、ここではNode.jsの対応に絞ります。

Node.jsでは、2つの変更が入りました。ひとつは、同梱のルート証明書の一覧を NSS 3.131.1 のものに更新したことです。この変更の作業記録(プルリクエスト #66877)によると、「emSign ECC Root CA – G3」「emSign Root CA – C1」「emSign ECC Root CA – C3」の3つのルート証明書が一覧から外されました。

2つの変更を表にまとめます。

変更内容作業記録
ルート証明書の更新NSS 3.131.1のものに更新。emSignの3つのルート証明書を一覧から外した#66877
拒否する公開鍵の一覧問題の中間証明書8枚の公開鍵の指紋(SHA-256)を本体に書き込み、鎖の確認のたびに照合する#66871

emSignはeMudhraの認証局の名前です。作業記録には、NSS 3.131.1 がまだFirefoxの正式版に入っていなかったため、ふだんの自動更新の仕組みでは取り込めず、手作業で版を指定して取り込んだとも書かれています。それだけ急いだ対応だったということです。

もうひとつは、「拒否する公開鍵の一覧」を持つ仕組み(プルリクエスト #66871)です。ブラウザには、問題のある証明書をあとから配信で無効にする仕組み(ChromeのCRLSetやFirefoxのOneCRL)がありますが、Node.jsにはこれに当たるものがなく、初期設定では証明書の失効をネット越しに確かめることもしていません。

そのため、中間証明書の鍵が漏れても、その上のルートが信頼されたままなら、鎖は有効と判断されてしまいます。かといってルートを外すと、同じルートの下にある正しい中間証明書まで使えなくなります。

そこでNode.jsは、問題の中間証明書8枚の公開鍵の指紋(SHA-256のハッシュ値)を本体に書き込み、証明書の鎖を確かめるたびに、その中に一致するものがないかを調べるようにしました。一致すると「失効した証明書」として扱われ、ふだんの検証の誤りと同じ道筋で拒否されます。作業記録によると、この指紋の一覧は、Go言語の修正(CVE-2026-111943)やElectronの修正と同じものだとされています。

注意

ここで覚えておきたいのは、この拒否の仕組みはNode.jsが作るすべての暗号化通信の設定に組み込まれる、という点です。–use-openssl-ca や –use-system-ca などのオプションでOSの証明書の一覧を使うようにしている場合でも、拒否の一覧による確認は行われると作業記録からは読み取れます。

ただし、作業記録には、更新の前に始まっていた通信の続き(セッションの再開)は確かめ直されず、そのセッションが時間切れになるまでは残る、という注意も書かれています。更新したら、プロセスを再起動するのが確実です。

同時に更新された部品

今回のリリースでは、ほかにも多くの部品が新しくなっています。系統ごとに違うので、表にまとめます。

部品役割26.12.124.22.122.23.4
ルート証明書信頼する認証局の一覧NSS 3.131.1NSS 3.131.1NSS 3.131.1
c-ares名前解決(DNS)1.34.91.34.91.34.9
undicifetch() などのHTTPの処理8.12.27.30.16.29.1
OpenSSL暗号の処理リリースノートに記載なし3.5.93.5.9
SQLite組み込みのデータベース記載なし記載なし3.53.4
タイムゾーンの情報各地域の時刻のずれ記載なし2026e2026e

このほか、3つの系統すべてに「X.509(証明書の形式)の変換と表示の処理を固くする」変更と、V8(JavaScriptを実行する部品)の修正の取り込みが入っています。26系では、ESモジュールの読み込み方についての以前の変更を取り消す修正も入りました。22系には、IPv6のアドレスを証明書のIPの欄と照らし合わせる修正も含まれています。

いずれもCVEの番号は付いていませんが、まとめて取り込むことになるので、更新後に自分のアプリの動きを一通り確かめておくと安心です。

更新の手順:まずは版を確かめ、nvm・Volta・Docker・CIで上げる

木の机の上のノートPCと外付けSSD

ここからは、実際に更新する手順です。Node.jsは入れ方がいくつもあり、同じパソコンの中に複数の版が入っていることもよくあります。まずは「どこで、どの版が動いているか」を確かめるところから始めましょう。

図:更新の手順の流れ
STEP 1動いている版を確かめるnode -v などで版を表示する
STEP 2版を切り替える道具で上げるnvm・nvm-windows・Volta・fnm
STEP 3Dockerのイメージを作り直す–pull を付けて作り直す
STEP 4CIの設定を確かめるnode-version を修正版にする
STEP 5再起動と作り直しプロセスの再起動、スナップショットやSEAの作り直し

1. 動いている版を確かめる

手元のパソコンでは、ターミナル(黒い画面)で node -v と入力すると、いま使われている版が表示されます。22系なら22.23.4以上、24系なら24.22.1以上、26系なら26.12.1以上になっていれば、今回の修正は入っています。

Node.jsに組み込まれている部品の版は node -p process.versions で一覧できます。ただし、今回のBrotliの修正は部品の版を上げるのではなく、修正部分だけを取り込む形(チェリーピック)で入っているため、process.versions のBrotliの欄の数字では判断できない可能性があります。判断はNode.jsそのものの版で行ってください。

複数の版が入っていそうなときは、macOSやLinuxなら which -a node、Windowsなら where node で、どこに入っているNode.jsが使われているかを確かめられます。エディタの拡張機能や、ほかのアプリに同梱されたNode.jsは、ここには出てこないこともあります。

サーバーやコンテナで動いているアプリは、手元の版とは別に確かめる必要があります。Impervaも、パッケージの一覧だけでは、コンテナの中に入っているNode.jsを見落とすことがあると注意しています。

コンテナなら docker run --rm イメージ名 node -v のように、実際に配っているイメージの中で版を表示させるのが確実です。動いているアプリの中で確かめたいときは、管理用の画面やログに process.version を出しておくと便利です。

2. nvm・nvm-windows・Volta・fnmで上げる

Node.jsの版を切り替える道具を使っている人は、その道具で新しい版を入れます。代表的な道具ごとの例は次のとおりです。

  • nvm(macOS・Linux):nvm install 24.22.1 で入れ、nvm alias default 24.22.1 でふだん使う版に設定します。系統の最新を入れるなら nvm install --lts や nvm install 22 も使えます。
  • nvm-windows:nvm install 24.22.1 のあと nvm use 24.22.1 で切り替えます。名前は似ていますが、nvmとは別の道具なので、細かい指定の仕方が少し違います。
  • Volta:volta install node@24.22.1 でふだん使う版を入れ替えます。プロジェクトごとに版を固定している場合は、そのプロジェクトの中で volta pin node@24.22.1 を実行し、package.json の volta の欄を更新します。
  • fnm:fnm install 24.22.1 と fnm default 24.22.1 で入れ替えます。

プロジェクトに .nvmrc や .node-version というファイルを置いて版を決めている場合は、そのファイルの中身も新しい版に書き換えておきましょう。ここが古いままだと、チームの別の人や、後で説明するCIが、古い版を入れ続けてしまいます。

package.json の engines の欄に版の条件を書いている場合も同じです。ただし engines の欄は、多くの場合「警告を出す」だけで、古い版での実行を止めるわけではない点に注意してください。

公式サイトのインストーラーや、Homebrew、Linuxの各ディストリビューションのパッケージで入れている場合は、それぞれの方法で更新します。ディストリビューションのパッケージは、Node.jsの公式の版番号ではなく、修正だけを取り込んだ独自の版番号になっていることがあります。その場合は、ディストリビューションのセキュリティ情報で、CVE-2026-111497などの番号が直っているかを確かめてください。

3. Dockerのイメージを作り直す

Dockerの公式のNode.jsイメージは、10月24日の時点で、26.12.1、24.22.1、22.23.4 の版の名前(タグ)が用意されています。公式イメージの一覧を管理するリポジトリでは、それぞれの系統に、Debianの bookworm と trixie(とその slim 版)、Alpine 3.23 と 3.24 の種類が並んでいます。

ここで大切なのは、Dockerfileの書き方によって、作り直しても新しい版にならないことがある点です。書き方ごとの注意は次のとおりです。

  • たとえば FROM node:24.22.0-bookworm-slim のように細かい版まで決め打ちしている場合は、この行を FROM node:24.22.1-bookworm-slim に書き換えなければなりません。
  • FROM node:24 のように系統だけを書いている場合でも、手元やビルドの環境に古いイメージが残っていると、それがそのまま使われることがあります。作り直すときは docker build --pull で元のイメージを取り直すようにしてください。
  • イメージの中身を固定するために、ダイジェスト(@sha256: で始まる値)で指定している場合は、その値も新しいものに更新が必要です。

セキュリティ専門の解説でも、ロックファイル(部品の版を記録したファイル)を更新したり、コンテナを再起動したりするだけでは足りず、修正済みの元のイメージから作り直す必要があると強調されています。

イメージを作り直したら、実際に動かす環境(本番、検証、ステージング)すべてに配り直し、古いイメージで動いているコンテナが残っていないかを確かめましょう。Kubernetesを使っている場合は、各Podのイメージの版を一覧にして確かめると漏れを防げます。

4. CIの設定を確かめる

GitHub Actionsなどの自動テスト・自動ビルドの仕組み(CI)でNode.jsを入れている場合も、確認が必要です。actions/setup-node で node-version に「24」や「24.x」のように系統だけを書いている場合、通常は新しい版が選ばれますが、キャッシュなどの都合で古い版が使われることがあります。

確実に最新を使いたいときは check-latest: true を付けるか、node-version に修正版の番号を書いてください。CIで作ったものをそのまま本番に配っている場合、CIの中のNode.jsの版が、そのまま本番の版になります。

5. 更新したら、再起動とスナップショットの作り直し

Node.jsは、起動したときに読み込んだプログラムでそのまま動き続けます。新しい版を入れても、動いているプロセスは古い展開処理を記憶したまま動いています。

PM2やsystemdなどでアプリを常に動かしている場合は、更新したあとに必ず再起動してください。また、前の節で書いたとおり、スタートアップスナップショットや単一実行ファイル(SEA)で配っているアプリは、新しい版のNode.jsで作り直してから配り直す必要があります。

すぐに更新できないときのつなぎの対策

どうしてもすぐに更新できない場合は、Brotliのデータが展開の処理に届く道を減らすことで、危なさを下げられます。ただし、Node.jsの公式は今回の10件について回避策を示しておらず、以下はあくまで更新までのつなぎです。

  • Expressでbody-parserの圧縮の自動展開が要らない場合は、express.json() などに inflate: false を指定して、圧縮された本文を受け付けないようにする方法があります。gzipなど、ほかの形式の展開も止まるので、正しい利用者に影響がないかを確かめてから使ってください。
  • Fastifyの@fastify/compressを使っている場合は、requestEncodings で受け付ける形式から br を外すことを検討します。
  • アプリの手前に置いたリバースプロキシ(nginxなど)やWAF(Webアプリを守る防火壁の仕組み)で、「Content-Encoding: br」の付いたリクエストを断る方法もあります。Impervaは自社のWAFにこの欠陥向けの規則を追加したとしています。
  • 外のサーバーの応答を fetch() で読む処理は、手前の守りでは防げません。WAFは、自分のアプリが外に取りに行った応答の中身までは見ないからです。また、リクエストの側で「Brotliは受け付けない」と伝えても、悪意のあるサーバーは構わずBrotliで返してくる可能性があります。利用者の指定したURLを取りに行く機能は、更新が済むまで一時的に止めることも検討してください。

Impervaは、展開の処理の途中で起きる予期しないプロセスの終了や、展開のエラーが、調べる優先度を決める手がかりになるとしています。ただし、どちらも単独では攻撃の確かな証拠にはならないとも書いています。ログにこうした記録が急に増えていないかを見ておくのは有益ですが、「記録がないから大丈夫」とは言い切れない点に注意してください。

Node.js 20以前、サポートの終わった版を使っている場合

倉庫の棚に置かれた古いタワー型サーバー

Node.jsの公式の発表には、毎回「サポートの終わった版(EOL、End of Life)は、セキュリティリリースのたびに必ず影響を受ける」という趣旨の決まり文句が書かれています。サポートの終わった版には修正が出ないので、欠陥が見つかるたびに、直らない欠陥がひとつずつ積み重なっていくという意味です。

セキュリティ企業の解説によると、Node.js 20は2026年4月30日に、Node.js 18は2025年4月30日に、すでにサポートが終わっています。16、14、12などの古い版も同様です。また、奇数の版(23や25など)は、もともと短い期間しか保守されない系統で、これらもすでに修正の対象外です。BrotliのCVE-2026-111497は、これらの古い版に組み込まれたBrotliにも残っているとみられ、修正は出ません。

まずは移行先を決める

サポートの終わった版を使っている場合の基本は、22・24・26のいずれかの修正版に移ることです。どの系統に移るかは、使っている部品が対応しているかと、今後の保守の期間で決めます。一般的には、長期サポートの24系が無難な選択です。

22系も修正は続いていますが、24系より先に保守の期間が終わります。26系は最新の機能が入る一方で、部品の対応が追いついていないこともあります。

大きく版を飛ばすと、使っている部品が動かなくなることがあります。特に、C言語やC++で書かれた部分を含む部品(ネイティブアドオン、たとえば bcrypt や sharp、古い node-sass など)は、Node.jsの版に合わせて作り直しが必要です。移る前に、手元やテスト用の環境で新しい版に切り替え、npm ci で部品を入れ直してテストを一通り動かしておくと、本番での思わぬ失敗を防げます。

すぐに移れない場合

業務の都合などで、どうしてもすぐに移れない場合もあると思います。そのときは、次のような方法で被害の範囲を狭めます。

  • 前の節の「つなぎの対策」で書いた方法で、外から来たBrotliのデータを展開しないようにする。
  • そのアプリが外部から直接アクセスできないように、置き場所や通信の経路を見直す。
  • コンテナの中で管理者(root)として動かさない、使わない権限を外すなど、乗っ取られたときの被害を小さくする。
  • 移行の予定日を決め、それまでの間、この件を「直っていない危険」として記録しておく。

なお、Node.jsの公式は、保守期間を過ぎた版について、OpenJSの「エコシステム持続可能性プログラム」に参加する企業から有料の支援を受けられると案内しています。古い版向けの修正版を独自に作って提供している会社もあり、そのひとつは、今回の件の修正版を24〜48時間のうちに出す見込みだとしていました。

こうした支援を使うかどうかは、費用と移行の手間を比べて判断することになります。いずれにしても、延命は移行までの時間を稼ぐ手段で、移行の代わりにはならないと考えておくのが安全です。

立場別にやること

2台のノートPCと小型サーバー、ルーターが並んだ机

ここまでの内容を、立場ごとにやることとして整理します。全部を一度にやる必要はありません。自分に当てはまる行から手をつけてください。

立場まずやること余裕があればやること
手元でビルドツールやテストを動かすだけの人node -v で版を確かめ、系統の修正版に上げる.nvmrc や Volta の固定の版を更新する
Webのサーバーを書いている人修正版に上げ、本番のプロセスを再起動する外から来たBrotliのデータを展開している場所を洗い出す
利用者の指定したURLを取りに行く機能がある人最優先で修正版に上げる。間に合わなければ機能を一時停止接続先のホスト名やIPの書き方も自分で確かめる
Dockerで配っている人修正版のイメージで –pull を付けて作り直し、全環境に配り直すイメージの中のNode.jsの版を自動で確かめる仕組みを入れる
スナップショットやSEAを配っている人修正版のNode.jsで作り直して配り直すUUIDの重なりが起きていなかったかを確かめる
権限モデル(–permission)を使っている人修正版に上げ、ワイルドカードの許可を見直すコンテナなど、ほかの分離の仕組みと組み合わせる
Node.js 20以前を使っている人22・24・26への移行の予定を決める移行までの間、外からのBrotliの展開を止める

個人で開発している人

趣味でNode.jsを使っている人の多くは、手元のパソコンで開発道具を動かしているだけだと思います。その場合、今回の欠陥で直接攻撃される心配は大きくありませんが、手間も小さいので、nvmやVoltaで新しい版を入れておきましょう。

自分で作ったWebアプリを公開している場合は、そのアプリが動いている場所(レンタルサーバー、クラウド、自宅のサーバーなど)のNode.jsも忘れずに確かめてください。手元を更新して安心してしまい、公開している側が古いまま、というのはよくある見落としです。

仕事でサービスを運営している人

仕事でNode.jsのサービスを運営している場合は、まず「どのサービスで、どの版のNode.jsが動いているか」の一覧を作ることから始めます。Webのサーバーだけでなく、裏で動いている定期処理やキューの処理、管理画面、社内向けの小さな道具なども対象です。そのうえで、外から来たBrotliのデータを展開している場所、特に利用者の指定したURLを取りに行く機能や、圧縮されたリクエストを受け付けるAPIを優先して更新します。

今回のように、組み込まれた部品の欠陥が見つかったとき、パッケージの一覧(package.json やロックファイル)を調べるだけでは、Node.js本体の版が分かりません。コンテナのイメージの中身を調べる道具(SBOM、ソフトの部品表を作る道具など)を使って、Node.js本体の版まで一覧にできるようにしておくと、次に同じことが起きたときの確認がずっと速くなります。

Node.jsの基礎を固めたい人へ

今回の欠陥を理解するうえでは、Node.jsのストリーム(データを少しずつ流しながら処理する仕組み)や、HTTPのサーバーとクライアントの動き、イベントループの考え方が土台になります。Brotliの展開処理がデータを少しずつ受け取りながら進むことや、使い回しの接続の見張りがイベントループの動きに左右されることは、こうした基礎を知っていると、ぐっと分かりやすくなります。基礎を体系的に学び直したい人には、次の本が手がかりになります。

『ハンズオンNode.js』(今村謙士 著、オライリー・ジャパン、2020年)は、Node.jsの基礎からWebアプリの開発、テスト、デプロイまでを、実際に手を動かしながら学ぶ入門書です。出版社の紹介ページでは、次のような特徴が挙げられています。

  • サーバーサイドの経験が少ないフロントエンドの開発者や、ほかの言語は知っていてもNode.jsは初めてという人を読者に想定し、業務のレベルでアプリを作れる知識を身につけることを目標にしています。
  • コールバック、Promise、async/awaitなどの非同期の書き方に加えて、EventEmitterやストリーム、clusterやworker_threadsによる並列の処理まで、Node.jsの動きの仕組みを章を立てて説明しています。
  • HTTPのサーバーとクライアント、Express、Next.jsを使ったWebアプリの開発や、Server-Sent Events・WebSocketなどのリアルタイム通信を扱っています。
  • Mocha・Jestなどによるテストとデバッグ、PM2やDockerを使ったデプロイ、npmとYarnによるパッケージの管理まで、運用に近い話題もまとめられています。

2020年の本なので、扱っているNode.jsの版は今より古く、権限モデルや単一実行ファイルのような新しい機能は載っていません。版の番号や細かいオプションは公式の説明書きで確かめつつ、仕組みの理解のために読むのがおすすめです。

ハンズオンNode.jsをAmazonで見る

まとめ

2026年10月21日、Node.jsは 22.23.4、24.22.1、26.12.1 のセキュリティリリースを公開し、10件の欠陥を修正しました。最も重いのは、Googleの圧縮形式Brotliの展開処理にあった境界外書き込み CVE-2026-111497 で、Node.jsの公式の評価はHigh、CVEの記録ではCVSS 4.0で9.2とされています。

通称は「Node2Shell」「HTTP2Shell」「Broccoli2Shell」などと揺れているので、連絡には番号を使うのが確実です。Impervaが公式のNode.js 24.22.0のイメージで命令の実行を再現した一方、10月24日の時点で実際の悪用は報告されていません。

残りのHighの3件は、TLSのホスト名確認のすり抜け2件とDNSの名前の切り詰め1件で、MediumとLowの6件には、使い回しの接続、スナップショットの乱数、ZIP、権限モデルの欠陥が含まれます。さらに、同梱のルート証明書がNSS 3.131.1に更新され、eMudhraの問題の中間証明書を拒否する仕組みも入りました。Brotliを使っていない人にとっても、更新する理由は十分にあります。

  • node -v で版を確かめ、22.23.4・24.22.1・26.12.1 のいずれか以降に上げる
  • 外から来たBrotliのデータを展開する場所(fetch()、Express 5・body-parser 2、@fastify/compress など)を優先して確かめる
  • Dockerのイメージは –pull を付けて作り直し、すべての環境に配り直す
  • 更新後はプロセスを再起動し、スナップショットやSEAは作り直す
  • Node.js 20以前を使っているなら、移行の予定を決め、それまでの間は展開の経路を絞る

同じ日に出たPHP2Shellと同じく、今回の欠陥も、言語の実行環境そのものに組み込まれた部品の問題でした。自分の書いたコードに欠陥がなくても、足元の実行環境が古ければ攻撃の入口になります。「どこで、どの版が動いているか」を把握しておくことが、次の同じような騒ぎのときにも、いちばんの備えになります。

出典

※この記事の内容は、2026年10月24日時点で公表されている情報をもとにしています。