【ITニュース解説】CVE-2026-109869: CVE-2026-109869: Denial of Service via Orphaned Stream in Node.js Undici Fetch Implementation
2026年10月24日に「Dev.to」が公開したITニュース「CVE-2026-109869: CVE-2026-109869: Denial of Service via Orphaned Stream in Node.js Undici Fetch Implementation」について初心者にもわかりやすく解説しています。
ITニュース概要
Node.jsのHTTP通信ライブラリ`undici`に、サービス拒否の脆弱性(CVE-2026-109869)が発見された。悪意のあるサーバー応答により内部処理がミスを起こし、Node.jsプログラムが強制終了させられる危険がある。速やかに`undici`を修正版にアップデートする必要がある。
ITニュース解説
CVE-2026-109869は、Node.js環境で広く利用されているHTTPクライアントライブラリ「Undici」に存在する、深刻度の高いサービス拒否(Denial of Service; DoS)の脆弱性である。この脆弱性が悪用されると、Node.jsで動作するアプリケーション全体が予期せずクラッシュし、サービスが停止してしまう重大な問題を引き起こす可能性がある。
まず、基本的な背景から説明する。Node.jsは、JavaScriptを使ってサーバーサイドのアプリケーションやAPIを構築するための実行環境であり、多くのWebサービスやバックエンドシステムで利用されている。Undiciは、そのNode.js環境でWebサーバーとのHTTP通信を行うための高性能なライブラリで、Webページからデータを取得したり、APIにリクエストを送ったりする際に内部的に使われることが多い。特にNode.jsのバージョン18以降では、Webブラウザでお馴染みのfetch()関数がグローバルに利用できるが、このfetch()関数のデフォルト実装にUndiciが採用されているため、Undiciの脆弱性はNode.jsを利用する多くのシステムに影響を及ぼす可能性がある。
このCVE-2026-109869の脆弱性は、UndiciがHTTPレスポンスを処理する際の手順に起因する。Webサーバーがクライアント(Node.jsアプリケーション)にデータを返す際、そのデータがどのように圧縮されているかを示すために「Content-Encoding」というHTTPヘッダーを使用することがある。例えば、gzipやbrといった圧縮方式が指定され、これにより通信量を削減できる。Undiciは、セキュリティとパフォーマンスの観点から、このContent-Encodingヘッダーが過度に多い場合(具体的には5つを超える場合)に、そのレスポンスの処理を拒否する内部的な制限を設けている。
しかし、この脆弱性があったバージョンでは、UndiciがContent-Encodingヘッダーの数をチェックし、制限を超える場合にエラーとして処理を行う前に、内部で「Readable stream(読み込み可能なストリーム)」という、データフローを扱うためのオブジェクトを先に生成してしまうという問題が存在した。ストリームは、大きなデータを効率的に少しずつ処理するために使われる重要な仕組みである。
攻撃者はこの挙動を悪用する。具体的には、悪意のあるHTTPサーバーが、Node.jsアプリケーションに対して、意図的に5つを超えるContent-Encodingヘッダーを含むHTTPレスポンスを送信する。さらに、そのレスポンスの本体データ(ボディ)をすべて送信し終える前に、サーバー側でネットワーク接続を一方的に切断する。
このような状況が発生すると、UndiciはContent-Encodingヘッダーが多すぎることを検知し、fetch()の処理をエラーとして拒否しようとする。しかし、先に生成されてしまっていた「Readable stream」は、その後のエラー処理の対象から外れてしまい、「孤立したストリーム(Orphaned Stream)」と呼ばれる状態になってしまう。この孤立したストリームは、エラーが発生した際にそのエラーを適切に処理するための「エラーリスナー」や、処理を強制的に中止するための「中止信号(abort signal)」が設定されていないため、予期せぬソケット(ネットワーク通信の終端)の切断によってストリーム内部でエラーが発生しても、そのエラーはどこにも捕捉されずに、結果としてNode.jsプロセス全体に伝播してしまう。最終的に、Node.jsアプリケーションはクラッシュし、サービスが停止する事態に至る。
この問題の特に深刻な点は、通常のアプリケーション開発者がJavaScriptコードで実装するtry/catchブロック(エラーを捕捉して処理する一般的な仕組み)では、この種の低レベルなストリームエラーを捕捉し、アプリケーションのクラッシュを防ぐことができなかった点である。つまり、アプリケーションコードでどれだけエラーハンドリングを丁寧に行っていたとしても、この脆弱性によるクラッシュを防ぐことは困難だった。
この脆弱性の深刻度を示すCVSSスコアは7.5であり、「高(High)」カテゴリに分類される。これは、攻撃がネットワーク経由で実行可能であり、特別な認証やユーザーの操作は不要であるため、攻撃の敷居が低いことを示している。攻撃の結果はサービス拒否に限定されるものの、システムの可用性が損なわれることはビジネス上、大きな影響をもたらす。既にこの脆弱性の「概念実証(Proof of Concept; PoC)」コードが存在しており、実際に攻撃が可能であることを示すコードが確認されているため、現実世界での悪用のリスクが高いとされている。
この脆弱性の影響を受けるのは、npmパッケージのundiciが特定のバージョン範囲にある場合、およびNode.jsのグローバルなfetch()実装を使用している場合である。具体的には、undiciのバージョンが6.23.0から6.29.1未満、7.18.0から7.30.1未満、または8.0.0から8.12.2未満のものが影響を受ける。
この脆弱性を修正するためのコード変更は、主にReadable streamの作成タイミングを変更することで実現された。修正前は、Content-Encodingヘッダーの数をチェックする前にストリームが作成されていたが、修正後は、まずヘッダーの数を厳密に確認し、制限を超過している場合はすぐにcontroller.abort()を呼び出して処理を中止するように変更された。これにより、脆弱性の原因となっていた孤立したストリームが生成されることがなくなり、エラーが安全かつ適切に処理されるようになった。
この脆弱性からシステムを保護するための最も重要な対策は、影響を受けるundiciパッケージのバージョンを、パッチが適用された最新バージョンに速やかにアップグレードすることである。具体的には、undiciのバージョンを6.29.1以上、7.30.1以上、または8.12.2以上に更新する必要がある。自身のプロジェクトでundiciがどのバージョンで使われているかを確認するには、npm ls undiciのようなコマンドを実行すると良い。もしNode.jsのグローバルなfetch()機能を使用している場合は、Node.jsランタイム自体のバージョンを更新するか、またはパッチが適用されたundiciパッケージを使ってグローバルなfetch()実装を上書きすることが推奨される。
さらに、アプリケーションレベルでのアップデートだけでなく、ネットワークレベルでの緩和策も有効である。例えば、Node.jsアプリケーションから外部へのHTTP/HTTPSリクエストを信頼できる特定のドメインに制限したり、プロキシサーバーを介して通信を監視し、過剰なContent-Encodingヘッダーを含むHTTPレスポンスをブロックしたりする方法も考えられる。しかし、最も直接的で根本的な解決策は、脆弱性が修正されたソフトウェアに更新することである。
この脆弱性は、Webアプリケーション開発において、外部からの入力(ここではHTTPレスポンスヘッダー)の検証、そしてリソース(ストリームなど)のライフサイクル管理、そして低レベルな部分におけるエラー処理の重要性を改めて示している。開発者は、常に使用しているライブラリやランタイムのセキュリティ情報を確認し、脆弱性が発見された際には迅速なアップデートを行うことが、安定したサービス運用には不可欠である。