2026年10月21日、Node.js プロジェクトは、同梱している圧縮ライブラリ Brotli(ブロトリ)のデコーダーに、メモリの範囲外へ書き込んでしまう欠陥があると公表しました。悪意ある圧縮データを処理させると、プログラムの異常終了、メモリ内容の漏えい、さらには遠隔からのプログラム実行にまでつながるおそれがあります。管理番号は CVE-2026-111497 で、セキュリティ各社は「Node2Shell」「Broccoli2Shell」「HTTP2Shell」などの通称で紹介しています。

直前に話題になった PHP の脆弱性(PHP2Shell) と並べて、「また別の大きな問題が出た」と感じた方も多いでしょう。ただ、今回の問題は PHP や Node.js だけの話ではありません。Brotli は Web の通信を軽くするために広く使われており、同じ部品を内蔵した製品が複数あることが、話を大きくしています。確認日は 2026年10月29日です。

この記事では、専門用語をできるだけ日常の言葉に置き換えながら、どこに問題があり、どの製品が影響を受け、何をすればよいのかを順番に説明します。

脆弱性

CVE-2026-111497。Brotli デコーダーの範囲外書き込みです。

危険度

Google(CNA)の評価は CVSS v4.0 で 9.2(Critical)です。

直す方法

Node.js は 22.23.4 / 24.22.1 / 26.12.1 以降へ更新します。

急ぐ理由

公開された攻撃コードがあり、CISA が悪用確認済みとして登録しました。

「圧縮データを読むだけ」では済まない理由

最初に、なぜこの脆弱性が大きく扱われているのかを整理します。気をつけたい点は 4 つあります。

理由 1: 攻撃に必要なのは「細工された圧縮データ」だけ

攻撃者がすることは、細工した Brotli 形式の圧縮データを、相手のプログラムに展開させることです。ログインやパスワードは要りません。Google は、攻撃の難しさや必要な権限を低く見積もり、CVSS v4.0 で 9.2 の緊急と評価しています。

理由 2: データの入り口が、思った以上に多い

圧縮データが届く経路は、Web サイトの閲覧に限りません。

  • 利用者が送ってくる、圧縮された通信の本体(リクエストボディ)
  • 自分のプログラムが他のサーバーから取得する、圧縮された応答(レスポンス)
  • 利用者が読み込ませる、Brotli で圧縮されたファイル

Webhook の受信や、利用者が指定した URL を読みに行く機能は、とくに入口になりやすいところです。

理由 3: 修正が 8 か月近く「見える場所」にあった

報道によれば、修正自体は 2026年2月27日に、Brotli の公開リポジトリへ取り込まれていました。しかし、そのときは新しい版の公開も注意喚起もなく、10 月に Node.js が公表するまで、約 8 か月がたっています。修正を読み取れば、欠陥の場所を推測できる状態が続いたことになります。

理由 4: 更新を受けられない古い版がある

Node.js の 20 系、23 系、25 系はすでにサポートが終わっており、今回の修正版は出ません。古い版のまま使っている場合は、公式の修正を待つのではなく、版そのものを移す必要があります。

悪用の状況はどうか

2026年10月27日、米国の CISA は、この脆弱性を「悪用が確認された脆弱性(KEV)」の一覧に追加しました。公開された概念実証(PoC)もあり、Node.js 24.22.0 での実行を示した報告もあります。

一方で、確実に攻撃できるわけではないとする検証も出ています。攻撃コードは Node.js の個別のビルドごとに調整が要るため、別のビルドでは、プログラムが落ちるだけに終わることが多いと報告されています。ただし、落ちたプログラムが自動で再起動する構成だと、攻撃者は何度でも試せます。「成功率が低いから放置してよい」という判断は、安全の根拠になりません。

先に結論: やることは「更新」と「再デプロイ」

不安をあおるだけでは役に立たないので、先に結論を書きます。

  1. 自分の環境で、Brotli を使う部品があるかを調べます。
  2. 該当する場合は、修正版へ更新します。
  3. 更新したら、プロセスを再起動し、コンテナの場合はイメージを作り直して再デプロイします。
  4. すぐに更新できない場合は、暫定対処で危険を下げます。

この流れを頭に置いて、以降を読み進めてください。

背景: Brotli とは何か

Brotli は、Google が開発したデータ圧縮の方式です。仕様は RFC 7932 で標準化されています。ZIP のように「大きなデータを小さく詰めて、受け取った側が元に戻す」仕組みの仲間だと考えてください。

Web では、サーバーがページや画像を圧縮して送り、ブラウザーが展開します。このとき使われる合図が、Accept-Encoding と Content-Encoding という 2 つの HTTP ヘッダーです。

ヘッダー 役割 Brotli の場合
Accept-Encoding 「この形式の圧縮なら受け取れます」という申告 br を含める
Content-Encoding 「このデータはこの形式で圧縮しました」という宣言 br と書く

つまり、Content-Encoding: br と書かれたデータを受け取ったプログラムは、Brotli のデコーダー(展開する部品)を動かします。この展開処理に欠陥があったというのが、今回の問題です。

clientserver

攻撃者: 細工した Brotli データを送る(Content-Encoding: br)
受け取った側のプログラムが、自動で展開しようとします
デコーダーが、確保した領域の外へ書き込んでしまう
メモリが壊れ、異常終了・情報漏えい・コード実行のおそれがあります

影響を受けるのは「展開する側」

大事な点を 1 つ挙げます。危険なのは、細工されたデータを展開する側のプログラムです。Brotli のデコーダーを持っているだけでは、直ちに危険とは限りません。攻撃者が用意したデータを、そのデコーダーが実際に読む経路があるかどうかが、分かれ目になります。この点は、セキュリティ会社の Rapid7 も指摘しています。

問題の箇所: 「データが足りない」と「データが壊れている」の取り違え

次に、どこに欠陥があったのかを説明します。Node.js の告知によると、原因は次のとおりです。

Brotli のデコーダーは、データを少しずつ受け取りながら展開します(ストリーム処理)。その途中で、データの区切りの切り替えを読む処理があります。細工されたデータでは、この切り替えがあり得ない内容になります。

本来なら、デコーダーは「このデータは壊れている」と判断して、すぐに処理を打ち切るべきです。ところが欠陥のある版では、これを「続きのデータがまだ届いていないだけ」と取り違えました。そのため、デコーダーは待ち続け、届いたデータを 8 バイトの内部の入れ物に追加し続けます。入れ物は 8 バイトしかないので、いずれあふれて、隣のメモリを上書きします。

たとえるなら、次のような状況です。

  • 本来は「この伝票は不正です。受け付けません」と断るべき
  • ところが窓口が「まだ書きかけなんですね」と受け取り続ける
  • 小さな引き出し(8 バイト)に、書類を詰め込み続けて、引き出しの外にあふれ出す

このように、確保した領域の外へ書き込む欠陥は、CWE-787(範囲外書き込み)と呼ばれます。Rapid7 は、これに加えて、エラーを示す戻り値の見落とし(CWE-252)も原因に挙げています。

どの版が対象か

Brotli の対象は、1.0.3 から 1.0.9、1.1.0、1.2.0 です。修正版は、それぞれ 1.0.10、1.1.1、1.2.1 になります。

影響を受ける製品: Brotli を内蔵したものすべて

Brotli は単体のソフトウェアというより、他の製品の部品として組み込まれていることがほとんどです。そのため、影響は広い範囲に及びます。修正済みの版を表にまとめます。

製品 影響を受ける版 修正版
Node.js 22.x、24.x、26.x の修正前の版 22.23.4 / 24.22.1 / 26.12.1
Bun 1.0.21 以上 1.4.4 未満 1.4.4
Python の Brotli 1.0.3 から 1.2.0 1.2.1
Python の brotlicffi 1.0.3 から 1.2.0.x 1.2.1.0
.NET 8 / 9 / 10 8.0.33 / 9.0.22 / 10.0.14 より前 8.0.33 / 9.0.22 / 10.0.14
Envoy Proxy 1.36.11 以前、1.37.7 以前ほか 1.36.12 / 1.37.8 / 1.38.6 / 1.39.3 / 1.40.0
Istio 1.29.9 / 1.30.6 / 1.31.2 より前 1.29.9 / 1.30.6 / 1.31.2

この表のほかにも、次のような製品が、影響を受けるものとして報告されています。

  • FFmpeg(JPEG XL のメタデータを経由する場合)
  • Apache Arrow(Parquet の読み込みで、無限ループによるサービス停止を招く問題)
  • ClickHouse(細工された要求 1 件で、検証したサーバーが停止した報告)
  • Python の HTTP クライアント(urllib3、requests、httpx、aiohttp)。Brotli や brotlicffi が入っている場合に、応答を自動で展開する

Node.js の 20 系、23 系、25 系は影響を受けますが、サポート終了のため修正版は出ません。

Node.js ではどこが入口になるか

Node.js の告知では、入口は node:zlib の Brotli 関数です。セキュリティ会社の解説では、具体的な場面として次が挙げられています。

  • Express 5 や body-parser 2.x: 利用者が Content-Encoding: br でボディを送ると、自動で展開します。素の Node.js の HTTP サーバーは、受信したボディを自動では展開しません。
  • fetch(): Brotli で圧縮された応答を自動で展開します。攻撃者が用意した(または乗っ取った)サーバーから、細工した応答が返ると影響を受けます。
  • 直接の展開: 信頼できない入力を、node:zlib の Brotli 関数に渡している場合です。

PHP への影響

先の PHP の話題とつなげて、PHP の場合も整理します。報道によれば、PHP プロジェクトは、公式の Windows 向けビルドが Brotli を内蔵しているため、Brotli 1.2.1 を含む再ビルド版(8.5.12-1、8.4.27-1、8.3.36-1、8.2.35-1)を公開しました。PHP の版番号そのものは変わらないため、php -v の表示は更新前と同じです。見分けるには、配布ファイル名の「-1」と、配布ページに載る SHA-256 の値を確認します。

届く経路は、cURL 拡張が HTTP の応答を自動で展開する設定にしている場合です。cURL の既定ではこの設定は切れていますが、アプリケーションや HTTP クライアントのライブラリが有効にしていることがあります。また、PHP の拡張モジュール brotli(kjdev 版)は、0.21.1 で修正されたと報告されています。

Linux 向けの PHP(ディストリビューションのパッケージや Docker イメージ)が、どの Brotli を使っているのかは、本記事で参照した資料からは確認できませんでした。利用中のディストリビューションやイメージの提供元が出す告知で、要確認です。

確認できていない製品

nginx の Brotli モジュール、Apache の mod_brotli、curl、ブラウザーについては、本記事の確認日時点で、参照した一次情報や主要な解説に、影響を受けるという記述を確認できませんでした。ただし、これは「影響がない」という確認ではありません。各提供元の告知で確認してください。

影響の大きさ: CVSS 9.2 と「High」の違いをどう読むか

深刻度の評価は、組織によって異なります。

評価した組織 評価 見方
Google(CNA) CVSS v4.0 で 9.2(Critical) Brotli 本体の欠陥としての評価
Node.js プロジェクト High Node.js 上での影響としての評価
Microsoft CVSS v3.1 で 8.1(.NET) .NET 上での影響としての評価

数値が違うのは、どこで使われるかによって、攻撃の難しさや影響の範囲が変わるためです。どれが正しいというより、自分の環境で、攻撃者のデータがデコーダーに届くかどうかを見ることが大切です。届くなら緊急、届かないなら優先度は下がります。ただし、届かないと断言するには、依存関係と通信経路を調べる必要があります。

実行されるのは「そのプログラムの権限」

遠隔でコードが実行される場合、動くのは展開を行ったプログラムの権限です。Web サーバーのプロセスなら、そのプロセスが読めるファイル、DB 接続情報、環境変数(API キーなど)が危険にさらされます。コンテナなら、まずそのコンテナの中が対象です。ここから先の被害の広がりは、権限の分け方や、ネットワークの分離の仕方に左右されます。

対応策 1: 修正版へ更新して、再デプロイする(本命)

最も確実な対策は、Brotli を含む部品を、修正版へ更新することです。Node.js 側は、これが公式に示された唯一の対策で、公式の回避策は挙げられていません。

自分の環境を調べる

次のコマンドで、版を確認できます。

# Node.js(ホスト、CI、コンテナの中)
node --version

# コンテナイメージごとに確認
docker run --rm <イメージ名> node --version

# Express 5 / body-parser 2.x を使っているか
npm ls express body-parser

# Python の Brotli 関連パッケージ
pip show Brotli brotlicffi

あわせて、ソースコードの中で brotliDecompress や createBrotliDecompress を使っていないかを検索します。

更新だけでは終わらない

更新の後に、次の作業が要ります。

  • プロセスの再起動: メモリに載っている古いプログラムは、ファイルを更新しても入れ替わりません。
  • コンテナの再ビルドと再デプロイ: 古いタグに固定したイメージは、そのまま脆弱です。
  • 自前で Brotli を同梱したソフトウェア: システムの部品を更新しても直りません。そのソフトウェア自身の修正版が必要です。
  • 実行中のものが修正版か確認: 更新の記録ではなく、動いているものを確認します。

対応策 2: 古い版を使っている場合

Node.js の 20 系、23 系、25 系には、修正版が出ません。選べる道は、サポート中の版へ移ることだけです。

  • 22 系 / 24 系 / 26 系へ移す: 修正版は 22.23.4、24.22.1、26.12.1 です。
  • 移せない間は、暫定対処を併用する: 次の節の対処を入れて、データの入口を減らします。
  • Docker イメージのタグを確認する: node:20 のような古いタグを、気づかずに使い続けていないかを調べます。

対応策 3: すぐに更新できないときの暫定対処

更新の準備が整うまでの危険を下げる方法です。これらは更新の代わりにはなりません。

圧縮された受信データを止める

手前のサーバー(リバースプロキシやロードバランサー)で、Content-Encoding: br の付いた要求を断る方法があります。nginx の例です。

server {
    # Brotli で圧縮された要求ボディを受け付けない
    if ($http_content_encoding = "br") {
        return 415;
    }
}

415 は「サポートしていない形式」を意味する状態コードです。この設定は、要求を断るだけなので、return と組み合わせた if として使えます。

他のサーバーから取る応答は別に対処する

上の方法では、自分のプログラムが他のサーバーから取得する Brotli の応答は守れません。その場合は、次の対処があります。

  • HTTP クライアントの設定で、Brotli の展開を無効にする(Accept-Encoding から br を外すだけでは不十分と報告されています)
  • Python では、Brotli と brotlicffi のパッケージを、修正版が入るまで外す
  • Envoy や Istio では、Brotli の展開フィルターを双方向で無効にする

暫定対処の落とし穴

暫定対処には、次の限界があります。

  • 受信の対処は、受信した要求しか守れません。取得した応答は別の対処が要ります。
  • 悪意あるサーバーに接続する機能(Webhook、URL の取り込みなど)は、更新まで止めるのが安全です。
  • 「自動で再起動するから大丈夫」と考えないでください。攻撃者に何度も試す機会を与えます。

更新後に確認すること

更新したら、次の点を確認して完了にします。

  1. 動作中のプロセスが、修正版であること(node --version など)。
  2. コンテナのイメージが再ビルドされ、古いタグが残っていないこと。
  3. 不審な再起動やクラッシュが、更新前に記録されていないか、ログを見直すこと。
  4. 手前のサーバーで入れた暫定対処は、不要になったら外すこと(外し忘れると、正当な圧縮要求も断り続けます)。

ログに、Content-Encoding: br の付いた不審な要求や、同じ時間帯に集中したクラッシュが残っていた場合は、更新と並行して、環境の点検を検討してください。

一度きりの騒ぎにしないために

今回の件は、Brotli だけの問題ではありません。「使っていることを意識していない部品が、重大な脆弱性を抱える」という、同じ型の問題が繰り返し起きます。備えとして、次の 3 点を勧めます。

  • 使っている部品の一覧を持つ: どのアプリが、どの版の Node.js や Python のパッケージを使うのかを把握します。コンテナなら、イメージごとに記録します。
  • サポート期限を管理する: 修正版が出ない版を使い続けることが、最も大きなリスクです。
  • 更新を自動化する: 週に一度などの頻度で、ベースイメージと依存パッケージの更新を取り込む仕組みを作ると、公開から対応までの時間が短くなります。

今回の件を通じて確認しておきたいこと

CVE-2026-111497 は、データを展開する小さな部品の判断ミスが、遠隔からのコード実行につながりうることを示しました。要点をまとめます。

  • 問題の箇所: Brotli のデコーダーが、壊れたデータを「続きがまだ届いていない」と取り違え、8 バイトの内部の入れ物からあふれて書き込みます。
  • 影響の大きさ: Google の評価は CVSS v4.0 で 9.2 です。CISA は 2026年10月27日に悪用確認済みとして登録しました。ただし、攻撃の成功率は環境によって大きく異なるとの検証もあります。
  • 対象: Node.js、Bun、.NET、Python の Brotli 関連パッケージ、Envoy、Istio など、Brotli を内蔵した製品です。
  • 対応: 修正版へ更新し、再起動または再デプロイします。古い版は、サポート中の版へ移します。
  • 更新までの暫定対処: 受信側では Content-Encoding: br を断り、取得側では Brotli の展開を止めます。

先に結論を書いたとおり、やることは「調べる、更新する、再デプロイする」です。まず、自分の環境で、攻撃者のデータが Brotli のデコーダーに届くかどうかを調べるところから始めてください。PHP の件とあわせて、使っている部品を一覧にしておくと、次の騒ぎでも落ち着いて対応できます。