戻る

2026/10/26

HTTP2Shell(CVE-2026-111497):バイナリを更新しても実行中のプロセスは修正されない

HTTP2Shellの原因となったBrotliの脆弱性はNode.js、Bun、Pythonに及びます。稼働中のプロセスで実際の影響を見極め、AIエージェントを安全に再起動する方法を解説します。

2026年10月21日、Node.jsはGoogleのBrotliデコーダーにおける境界外書き込みの脆弱性CVE-2026-111497の修正版をリリースしました。数日のうちにBun、Pythonのbrotliおよびbrotlicffiパッケージ、Envoy、Istioからもアドバイザリが続き、セキュリティメディアはこの脆弱性をNode2Shell、その後HTTP2Shellと名付けました。名前に反して、これはHTTP/2プロトコルのバグではありません。展開(解凍)処理のバグであり、攻撃者が影響を与えうるBrotliデータをソフトウェアがデコードするあらゆる場所で問題になります。

本記事では、確認済みの事実と可能性にとどまる事項を区別し、自社システムの確認方法を説明します。また、当社自身の対応から得た教訓も取り上げます。それは、修正済みのバイナリをインストールしても、古いバイナリを実行し続けているプロセスは修正されないということです。当社の環境では、インストール済みのClaude Codeを更新した後も、長時間稼働しているClaude Codeセッションが古い組み込みランタイムを保持したままでした。

要点

  • 影響を受けるのはアップストリームのBrotli 1.0.3~1.2.0です。修正版は1.0.10、1.1.1、1.2.1です。Brotliを同梱する製品には、それぞれの修正版リリースが必要です。
  • 実際に影響を受けるには、攻撃者が影響を与えうるBrotliデータが脆弱なデコーダーに届く経路が必要です。製品名やネットワークアクセスだけでは証拠になりません。
  • インストール済みパッケージだけでなく、稼働中のプロセスを確認してください。プロセスは再起動されるまで、起動時のランタイムを使い続けます。
  • 状態を持つAIエージェントは、イベント、承認、作業状態を先に引き継いだうえで、1つずつ再起動してください。

1. 脆弱性の内容

デコーダーは不正なブロック切り替えに遭遇した際、恒久的なフォーマットエラーではなく「入力がさらに必要」と報告していました。そのためストリーミングで呼び出す側はバイトを送り続け、デコーダーはそれを8バイトの内部バッファーに追記し、末尾を越えて書き込んでいました。Node.jsのアドバイザリによると、アプリケーションがnode:zlibで展開するBrotliデータを供給できる攻撃者は「プロセスメモリを破壊でき、クラッシュ、プロセスメモリの漏えい、またはリモートコード実行につながる可能性がある」とされています。

深刻度の評価は情報源によって異なります。Node.jsはHigh(高)と評価しています。Pythonパッケージ向けのGitHubアドバイザリとEnvoyのアドバイザリはCritical(緊急)、CVSS 4.0スコア9.2としています。Ubuntuは現在、優先度Mediumとしています。10月26日時点で、CVEレコード上のCISAの評価では悪用状況は「none」とされ、このCVEはCISAのKnown Exploited Vulnerabilities(KEV)カタログに掲載されていません。それでもNode.jsのアドバイザリはリモートコード実行を起こりうる結果として挙げているため、攻撃の報告がないことを待つ理由にしないでください。

2. 前提条件:実際に影響を受けるのはどんなときか

次の3つの条件がすべて成り立つ必要があります。

  1. 脆弱なデコーダーが読み込まれている:プロセス内に、修正やバックポートのない、影響範囲内の同梱版またはシステムのBrotliライブラリがある。
  2. それを呼び出す処理がある:明示的な展開処理、またはContent-Encoding: brの自動デコード。
  3. 入力が攻撃者の影響を受けうる:任意のURLからのレスポンス、アップロードされたファイル、受信したリクエストボディ、または敵対的なネットワーク上の平文HTTP通信。

最も見落とされやすいのが自動デコードの経路です。Bunのアドバイザリによると、fetchはデフォルトでAccept-Encoding: brを送信してレスポンスをデコードするため、信頼できないURLを取得するBunプログラムにはすべて経路が存在します。Pythonでは、brotliまたはbrotlicffiがインストールされるとurllib3がContent-Encoding: brのレスポンスを自動的にデコードし、requestsもリクエストでbrを提示します。これは自社のコードでBrotliに一切触れていなくても起こります。EnvoyとIstioは、Brotliデコンプレッサーが設定されている場合にのみ影響を受けます。

3. 影響が確認されたものと、可能性にとどまるもの

2026年10月26日時点の状況です。状況は急速に変化しているため、リンク先のアドバイザリを確認してください。

コンポーネント状況修正バージョン
アップストリームのBrotli(C)確認済み:1.0.3~1.2.01.0.10、1.1.1、1.2.1
Node.js 22、24、26確認済み22.23.4、24.22.1、26.12.1
Node.js 20以前(サポート終了)評価対象外。Node.jsはサポート終了ラインを常に影響ありとして扱うサポート対象のラインへ移行
Bun(コンパイル済み実行ファイルを含む)確認済み:1.0.21~1.4.31.4.4、およびコンパイル済みアプリの再ビルド
Python brotli / brotlicffi確認済み:1.0.3以降1.2.1 / 1.2.1.0
Envoy(Brotliデコンプレッサー設定時)確認済み1.40.0、1.39.3、1.38.6、1.37.8(サポート外の1.36ブランチでは1.36.12)
Istio(Brotli展開有効時)確認済み1.31.2、1.30.6、1.29.9(1.29の最終リリース)
Ubuntuのbrotliパッケージ評価が必要(全リリース)修正版パッケージはまだなし
Ubuntuのnodejsパッケージ20.04では脆弱(Brotliを同梱)。22.04以降はシステムのBrotliを使用するため、上の行に準じるUbuntuのトラッカーを参照
Debianのbrotliパッケージbookwormとtrixieでは修正済み。forkyは未修正1.0.9-2+deb12u1、1.1.0-2+deb13u1
Chrome、ElectronおよびElectronアプリ不明:ベンダーの声明は見つからずベンダーのリリースノートを注視

最後の行について補足します。ChromeとElectronは独自のBrotliのコピーを同梱しており、ElectronアプリはさらにNode.jsランタイムも組み込んでいます。執筆時点で、Electronの最新安定版(45.0.0、44.9.3、43.8.3、いずれも10月21日)はNode.js 24.22.1ではなく24.22.0を記載しており、リリースノートにもこのCVEへの言及はありません。10月23日付けのElectronプレリリースにはNode.js 24.22.1が含まれているため、安定版での修正も見込まれますが、まだリリースされていません。したがって、Electronベースのデスクトップアプリは影響を受ける可能性がある段階であり、脆弱性が確認されたわけではありません。チャットクライアントやAIデスクトップツールも同様です。影響ありとも安全とも判断する前に、各ベンダー自身の声明を確認してください。

もう1つの落とし穴があります。報告されるライブラリのバージョンは、どちらの判断の証拠にもなりません。修正済みのNode.js 22.23.4でも、修正がバックポートされているためprocess.versions.brotliは1.1.0と表示されます。ディストリビューションも同様です。Brotliのバージョン文字列ではなく、製品の修正バージョンやディストリビューションのセキュリティステータスで判断してください。

4. 当社環境で判明したこと

当社では、何かを変更する前に読み取り専用の棚卸しを実施しました。以下は観察結果を匿名化したもので、確認済みの事実と検証できなかったリスクを分けて記載しています。

確認済みの観察結果

  • インストール済みのClaude Codeはすでに更新されていましたが、十数個の長時間稼働セッションが、数週間前に起動された古いビルドのまま動いていました。これらは一度も再起動されていませんでした。
  • 最初の集計では、そのうち3つを見落としていました。別のヘルパープロセスの下で動いていたため、コマンドラインによる検索では一致しなかったのです。実行ファイルで検索したところ見つかりました。
  • 古いビルドのファイルはアップデーターによってすでにディスクから削除されていたため、それらのプロセスに組み込まれたBunのバージョンは読み取れませんでした。
  • パッケージマネージャーでインストールされた古いClaude Codeビルドが残っており、Bun 1.4.0を組み込んでいました。通常運用では使われていませんでしたが、フォールバック経路で使われる可能性がありました。何も依存していないことを確認したうえで削除しました。
  • 使われていないサポート終了済みのNode.jsランタイムが、あるサーバーにまだインストールされていました。同じ手順で削除しました。
  • アップグレード先の現行Claude Codeビルド(2.1.310)にはBun 1.4.4が含まれています。これはバイナリを調べて確認しました。AnthropicのchangelogにはこのCVEへの言及がないため、ご自身のビルドも同じ方法で確認してください。

未検証のため、リスクとして扱ったもの

  • 古いセッションが実際に脆弱だったかどうか。Bunのバージョンは読み取れませんでした。修正版のBun 1.4.4が登場したのは10月22日頃で、それらのビルドより後だったため、影響ありとして扱いました。
  • 攻撃者がそれらのセッションを通じて、たとえば悪意のあるURLを取得させることでデコーダーに到達できたかどうか。Bunのfetchの動作として文書化されている内容からこの経路はありうるものですが、実証はしていません。
  • 独自のBrotliのコピーを同梱するその他のソフトウェアが影響を受けるかどうか。ベンダーの声明がない場合は、安全とも脆弱とも決めつけず、ベンダーのリリースに照らして製品を追跡しています。

悪用の証拠は見つかりませんでした。ただし、それを安全の証明とはみなしませんでした。対策は影響の可能性を理屈で否定することではなく、それ自体を取り除くことでした。

5. 検出と修正のチェックリスト

依存関係を見つける

パッケージとしてインストールされている場所だけでなく、同梱されている場所でもBrotliを探してください。SBOMやコンテナイメージで、Node.js、Bun、Pythonパッケージ、Envoy、静的リンクされたBrotliを確認します。Brotliはオプションのextraとして入り込むことが多いため、Pythonの推移的依存関係も確認してください。

# Node.js: compare with 22.23.4 / 24.22.1 / 26.12.1
node -p process.version
# Python packages: run once per interpreter and virtual environment
python -m pip list 2>/dev/null | grep -iE '^(brotli|brotlicffi) '
pipdeptree -r -p brotli          # what pulled it in
# Debian / Ubuntu system library (then check the distro tracker)
dpkg-query -W -f='${db:Status-Abbrev} ${Package} ${Version}\n' 'libbrotli*'
# Container images, with an SBOM scanner such as Syft
syft <image> | grep -iE 'brotli|node|bun'

組み込みランタイムを見つける

bun build --compileでビルドされたプログラムは、独自のBunのコピーを持っています。Bunのアドバイザリによると、これらは1.4.4で再ビルドされるまで影響を受けたままです。ビルドマシンのBunをアップグレードしても、すでにデプロイされたバイナリは変わりません。

# The Bun version inside a compiled executable
grep -aoE 'Bun v1\.[0-9]+\.[0-9]+' /path/to/binary | sort -u

稼働中のプロセスを見つける

インストールされているものではなく、実行されているものを確認してください。Linuxでは、実行ファイルが置き換えられたり削除されたりしたプロセスには(deleted)と表示されます。これは、まだ古いビルドを実行していることを強く示すサインです。

# Processes still running a deleted (replaced) executable (run as root to see all users)
for p in /proc/[0-9]*; do
  e=$(readlink "$p/exe" 2>/dev/null) || continue
  case "$e" in *"(deleted)") echo "${p#/proc/} $e";; esac
done
# Processes older than the fix
pgrep -af 'node|bun|claude'      # then: ps -o pid,lstart,args -p <pid>

macOSでは、lsof -p <pid>でプロセスが読み込んだ実行ファイルを確認できます。プロセス名ではなく実行ファイルで照合してください。そうしないと、ヘルパーやバックグラウンドワーカーを見落とします。

修正、再ビルド、再デプロイ

  • 表の修正バージョンにアップグレードしてください。バックポートがある場合は、ディストリビューションのパッチ適用済みパッケージを使用してください。
  • コンテナイメージとコンパイル済み実行ファイルを再ビルドし、再デプロイしてください。ベースイメージにパッチを当てても、再ビルドしていないイメージには何の効果もありません。
  • 使われていないランタイムや残存インストールは、どのサービス、ジョブ、スクリプトも依存していないことを確認したうえで削除してください。ロールバック手段は残しておきます。
  • 修正がまだない場合は、安全に行える範囲で、信頼できない入力のBrotliデコードを無効にしてください。Bunではfetch(url, { decompress: false })がリクエスト単位で使えます。各緩和策には担当者と終了期限を設定してください。

再起動と検証

再起動後は、新しいプロセスの実際の実行ファイルとランタイムバージョンを確認してください。次に業務上の出力を確認します。キューが処理し尽くされているか、スケジュールされたジョブが成功しているか、監視アラートが届いているかです。アップグレードコマンドが成功しても、それはコマンドが実行されたことしか証明しません。

6. 常駐型AIエージェントを安全に再起動する

長時間稼働するAIエージェントは、保留中のツール呼び出し、承認、キューに入ったメッセージ、作業の担当といった状態を保持しています。すべてを一度に再起動すると、その状態が失われる可能性があります。当社では次の手順がうまく機能しました。

  1. 各セッションを棚卸しする:実行ファイルのバージョン、状態(アイドル、待機中、処理中)、最終アクティビティ、保留中の作業、待っている承認や保持している承認、未確認のイベントと、その復旧方法。
  2. 承認をメモリの外に移す。セッションの会話の中にしか存在しない承認は、再起動で失われます。再起動の前に、保留中の各承認(何が、誰によって、どの変更に対して承認されたか)を、チケットやプルリクエストなど永続的な場所に記録してください。再起動後は、セッションがその承認を引き続き認識していることを確認します。疑わしい場合は、承認済みと決めつけず、改めて承認を依頼してください。
  3. まず代替手段が機能することを確認する。修正済みビルドのセッションがすでに正常に動作していること、そして古いセッションから独立したイベントウォッチャーが稼働していることを確認し、どのイベントも再起動されるセッションに依存しないようにします。
  4. アイドル状態のセッションから、1つずつ再起動する。1つ終えるごとに、古いプロセスが消えていること、新しいプロセスが修正済みの実行ファイルで動いていること、会話履歴が保たれていること、リモート接続が復帰していることを確認します。それから次に進みます。
  5. 最も負荷の高い担当セッションは、タスクの合間に最後に再起動する。引き継ぎメモを書かせ、保留中のイベントと未確認のイベントがゼロになってからウォッチャーを停止し、再起動します。その後、ウォッチャーが保存された位置から再開し、取りこぼしがないことを確認します。
  6. 再接続時にキューに溜まった入力が届くことを想定する。当社で再起動したセッションの1つは、10日間待機していたメッセージを受け取り、それに基づいて動作しました。再起動前に各セッションの受信箱を確認し、その担当者に伝えてください。
  7. いずれかの手順で作業が復旧したことを示せない場合は、中止してエスカレーションする。すべてを強制終了するという手段に頼らないでください。

始める前に「完了」を定義してください。完了とは、影響を受けるビルドで動いているプロセスが1つもなく、すべての例外が担当者と再確認日付きで文書化され、業務上のチェックが通っている状態を指します。「パッケージをアップグレードした」は完了ではありません。

参考資料