こんにちは!日々のインフラ運用やWeb開発、本当にお疲れ様です。技術メディア主筆の私です。
突然ですが、みなさんは「HTTP/2」という言葉を聞いたとき、どんなイメージを持ちますか?「Webサイトの読み込みが速くなる次世代のプロトコルでしょ?」というイメージが強いかもしれませんね。そう、HTTP/2の目玉機能である「マルチプレクシング(多重化)」は、1本の太いパイプライン(TCPコネクション)の中で、複数のリクエストやレスポンスを同時に、まるで合体技のようにスイスイとやり取りできるようにしてくれる魔法のような仕組みです。
でも、ちょっと待ってください。
「同時にたくさんのことができる」ということは、一歩間違えると……悪意ある攻撃者にとって「サーバーをいじめる絶好のチャンス」になってしまうのをご存知でしょうか?
今回は、HTTP/2の根幹を揺るがす恐ろしい脆弱性「ストリーム多重化攻撃」について、パケットの世界へ一歩踏み込んで、一緒に優しく紐解いていきましょう!
—
郵便局の窓口で起きた「大混乱」にたとえてみよう
難解なネットワーク用語をいったん脇に置いて、身近な世界にたとえてみましょう。
HTTP/1.1という昔の仕組みは、いわば「1つの窓口に1人ずつ並ぶ郵便局」でした。「荷物を送る(リクエスト)」、そして「返事を受け取る(レスポンス)」が完了するまで、次の人は後ろでじっと待たなければいけません。これでは混雑してしまいますよね。
そこで登場したHTTP/2は、「超高速で動き回るマルチ配達員(ストリーム)」を大量に雇い入れました。1本の大きな道路(TCPコネクション)を行き交う1人の配達員が、いくつもの荷物を同時に抱えてパタパタと走り回れるようになったのです。これがマルチプレクシングです。
非常に効率的で素晴らしい仕組みなのですが、ここに悪意を持った「迷惑な客」がやってきたと想像してください。
その客は、こんな行動に出ます。
「すいませーん、この小さな封筒の配達を、同時に1万件お願いしまーす!」
配達員たちは「えっ、そんなに!?」と慌てて1万件分の荷札を準備し、カバンに詰め込み、サーバー(郵便局の奥の倉庫)へ走ります。サーバー側は、たった1人のために1万件分の引き出しや台帳を急遽オープンしなければなり না。結果どうなるでしょうか?
サーバーは処理しきれなくなり、真面目な他のお客さんの荷物まで処理できなくなり、やがて「プシューッ……」と力尽きてダウンしてしまうのです。
これが、今回テーマにする「ストリーム多重化攻撃(HTTP/2 Stream Multiplexing Attack / Rapid Resetなど)」の正体です。
—
HTTP/2の「ストリーム」と「設定(SETTINGS)」の裏側
一歩ずつ理解していきましょう!
HTTP/2の世界では、1つの通信路(コネクション)のなかを、細かく番号が振られた小部屋のような通り道に分割して使っています。これを「ストリーム」と呼びます。
ブラウザが画像やCSS、JavaScriptを読み込むとき、HTTP/2は次のように考えます。
「よし、画像はストリーム番号『1』、CSSは『3』、JSは『5』を使って同時に取りに行こう!」
このストリームは、作ろうと思えば理論上、数百万個も同時に作れてしまいます。しかし、サーバー側のメモリやCPUは無限ではありません。無制限にストリームを作られてしまうと、サーバーはすぐにキャパシティオーバーになってしまいます。
ここでサーバー側の「自衛手段」として用意されているのが、SETTINGSフレーム(セッティングス・フレーム)という特殊な制御信号です。
サーバーの盾となる「SETTINGS_MAX_CONCURRENT_STREAMS」
サーバーは、クライアントと接続が確立した直後に、こう宣言します。
> 「うちの郵便局、今すごく混み合ってるからさ、1人が同時に開けるストリーム(配達員)は最大でも100人までね!それ以上は受け付けないから!」
この「同時ストリーム数の上限値」を定めているのが、HTTP/2の仕様にある `SETTINGS_MAX_CONCURRENT_STREAMS` というパラメーターです。
もし攻撃者がこの制限を無視して、次から次へと新しいストリームを作り続けようとしても、まともなサーバーであれば「おっと、上限の100を超えているからこのストリームは即座にキャンセル(RST_STREAM)しよう」と弾き返すことができます。
—
実務でどう守る? Nginxでの具体的な設定例
「じゃあ、実際に現場のインフラエンジニアはどうやってこの攻撃からサーバーを守ればいいの?」という疑問がわきますよね。
よく使われるWebサーバーであるNginxを例に、具体的な設定を見てみましょう。設定ファイル(`nginx.conf` など)の `http` ブロックや `server` ブロックに、以下のようなパラメータを記述してサーバーの身を守ります。
http {
# HTTP/2に関する詳細なチューニングとセキュリティ設定
# 1. クライアントが1つの接続内で同時に開けるストリーム数の上限を制限します
# デフォルト無制限に近い状態から、例えば「128」などに厳しく制限してリソース枯渇を防ぎます
http2_max_concurrency 128;
# 2. 1つのHTTP/2接続で処理できる最大リクエスト数(必要に応じて制限)
# 長く繋がりっぱなしのコネクションを定期的にリフレッシュさせます
keepalive_requests 1000;
# 3. クライアントからのデータ送信スピードが遅すぎる場合に切断するタイムアウト
client_body_timeout 10s;
client_header_timeout 10s;
}
このように、「ひとりのユーザーが調子に乗って同時に開けるお部屋の数(ストリーム数)を、あらかじめピシッと制限しておくこと」が、インフラを守るための第一歩になります。
さらに最近では、CloudflareなどのCDNや最新のWebサーバーにおいて、短時間にストリームを作っては即座にリセット(キャンセル)を繰り返す新しいタイプの攻撃(例:CVE-2023-44487 / Rapid Reset 攻撃)に対抗するため、「一定時間内にキャンセルできるストリームの回数(レートリミット)」を監視・遮断する高度な防御機構も組み込まれています。
—
まとめ:便利さとリスクは表裏一体
今回は、HTTP/2の便利な仕組みである「ストリーム多重化」の裏に潜む、リソース枯渇の脆弱性と、その対策についてお話ししました。
- HTTP/2のマルチプレクシングは複数のリクエストを同時に処理できる便利な仕組み。
- しかし、無制限にストリームを作らせると、サーバーのメモリやCPUが枯渇してダウンしてしまう(ストリーム多重化攻撃)。
- サーバー側は `SETTINGS_MAX_CONCURRENT_STREAMS` などの設定で同時ストリーム数に上限を設け、適切にガードすることが極めて重要。
新しいプロトコルや便利な技術が登場するとき、そこには必ず「利便性」と「セキュリティ(リスク)」のトレードオフが存在します。
「なぜこの設定が必要なのか」「このパラメータをいじるとパケットの世界で何が起きるのか」をイメージできるようになると、インフラやネットワークのトラブルシューティングが何倍も楽しく、そして強くなりますよ。
それでは、また次回の技術解説でお会いしましょう!安全で快適なネットワークライフを!
コメント