【実務・中級編】HTTP/2のセキュリティ脆弱性:HPACK圧縮爆弾 – HTTPプロトコル・通信規格実践ガイド

HTTP/2の静かなる脅威:HPACK圧縮爆弾(HTTP/2 Cancellation / Rapid Resetの系譜とメモリ防衛術)

夜中の3時、突然鳴り響くPagerDutyのアラート。
「API Gatewayのメモリ使用率が98%を突破、コンテナがOOM Killer(Out of Memory)により次々と強制終了しています!」

現場のインフラエンジニアなら誰もが冷や汗を流すこの光景。原因を調査すると、CPU負荷は拍子抜けするほど低いのに、メモリだけが数ギガバイト単位で一瞬にして喰らい尽くされている。トラフィック量を調べてもDDoSと言えるほどのメガbps/Gbpsは流れていない……。

犯人は、パケットの見た目は極めてお行儀が良いが、中身が極限まで圧縮された悪意あるリクエスト――「HPACK圧縮爆弾(Compression Bomb)」です。

今日は、HTTP/2の高速化の裏側に潜むこの厄介な脆弱性について、パケットの挙動から実際の防衛設定、コードレベルの対策まで、現場のシニアの視点で徹底的に紐解いていきましょう。

—

1. なぜ高速化の仕組みが凶器に変わるのか?

HTTP/2の最大の発明の一つが、マルチプレクシングと「HPACK(RFC 7541)」によるヘッダー圧縮です。

HTTP/1.1の時代、毎リクエスト数キロバイトにも及ぶ `User-Agent` や `Cookie` などの重複したヘッダーをプレーンテキストで送り続けるのは、回線の無駄遣い(ヘッダー肥大化問題)でした。これを解決するため、HTTP/2は「動的テーブル(Dynamic Table)」を導入しました。

[クライアント] —- (インデックス化されたヘッダー) —-> [サーバー / プロキシ]
│ │
├─ 1回目の送信: “user-agent: Mozilla/5.0…” (テーブルに登録: ID 62)
└─ 2回目の送信: “ID: 62” (たった数バイトで送信完了!)

この仕組みは実にエレガントですが、ここに「非対称性(Asymmetry)」という致命的な罠が潜んでいます。

  • 攻撃者のコスト: 数バイトの巧妙に細工された圧縮データを作るだけ(CPU負荷ほぼゼロ)。
  • 受信者のコスト: 展開(解凍)のために、サーバー側で動的テーブルのメモリ領域を大きく確保し、文字列を復元し続けなければならない(メモリ爆発)。

この特性を悪用し、サーバーのメモリリソースを枯渇させる手法がHPACK圧縮爆弾です。

—

2. HPACK圧縮爆弾の通信フローとメカニズム

では、この攻撃がネットワーク上でどのように行われるのか、パケットとシーケンスの裏側を覗いてみましょう。

Attacker (クライアント) Server / API Gateway
│ │
│── HEADERSフレーム (極限まで圧縮された巨大データ) ───►│
│ (SETTINGSフレームで動的テーブルサイズを最大化要求) │
│ │
│ [HPACKデコーダー]
│ ・動的テーブルを拡張
│ ・メモリ割り当て爆発
│ ・OOM Killer発動💥
│ │
│◄── RST_STREAM (CANCEL) / Connection Closed ─────────│

攻撃者は、HTTP/2の `SETTINGS` フレームを悪用し、サーバーに対して「私の動的テーブルの許容量(`SETTINGS_HEADER_TABLE_SIZE`)を最大値まで広げてください」と要求します。サーバーがこれを受け入れると、攻撃者はわずか数バイトのHuffman符号化されたハフマンツリーを送り込みます。

これがデコーダーによって展開されると、数MB、場合によっては数十MBの巨大なヘッダー文字列に膨れ上がり、それが数千のストリームから同時に流れ込んでくるのです。CPUは「展開するだけ」なので悲鳴を上げません。気づいた時にはメモリがゼロになっています。

—

3. 実務で直面するパラメーターとリスク

この脆弱性や類似の資源枯渇攻撃(HTTP/2 Rapid Resetなど)を防ぐためには、HTTP/2の裏側を支えるSETTINGSパラメーターを正しく理解し、デフォルト値のまま放置しないことがインフラエンジニアの絶対条件です。

主要な制御パラメーター

| パラメーター名 | RFC上の定義 | 危険なデフォルト値・設定 | 推奨される実務設定 |
| :— | :— | :— | :— |
| `SETTINGS_HEADER_TABLE_SIZE` | 動的テーブルの最大サイズ (オクテット) | 4096 (4KB) 〜 無制限に拡大許可 | 原則 4096 のまま、または必要最小限に固定 |
| `SETTINGS_MAX_CONCURRENT_STREAMS` | 同時オープン可能な最大ストリーム数 | 無制限(実質クライアント依存) | 100 〜 250 程度に厳格に制限 |
| `SETTINGS_INITIAL_WINDOW_SIZE` | フロー制御ウィンドウの初期サイズ | 65,535バイト (64KB) | 用途に応じて調整(大きすぎに注意) |

—

4. 【実践】NGINX / Envoy / Go言語における防衛設定とコード例

口で言うだけではなく、実際に現場のプロダクション環境でどのように設定・コード記述すべきかを見ていきましょう。

A. NGINXにおけるHPACK/HTTP/2リソース制限設定

リバースプロキシやAPI GatewayとしてNGINXを使っている場合、`http`ブロックまたは`server`ブロックで以下のようにバッファとテーブルサイズを絞ります。

http {
# HTTP/2接続全体の動的ヘッダーテーブルサイズをデフォルトの4kに厳格に制限
# 攻撃者による巨大テーブルの要求をここでシャットアウトします
http2_max_field_size 4k;
http2_max_header_size 16k;

# 同時ストリーム数を制限し、リソースの乱獲を防ぐ
http2_max_concurrent_streams 128;

server {
listen 443 ssl http2;
server_name api.example.com;

# SSL/TLS設定は省略…

location / {
proxy_pass http://backend_cluster;

# プロキシ側のバッファサイズも過剰に持たせない
proxy_buffering on;
proxy_buffer_size 4k;
proxy_buffers 8 4k;
}
}
}

B. Go言語 (net/http) による堅牢なHTTP/2サーバー実装

自社でAPIサーバーやマイクロサービスをGoで構築している場合、標準ライブラリの `http.Server` に `Http2` の設定を明示的に注入し、メモリ枯渇を防ぎます。

package main

import (
“log”
“net/http”
“golang.org/x/net/http2”
)

func main() {
mux := http.NewServeMux()
mux.HandleFunc(“/health”, func(w http.ResponseWriter, r http.Request) {
w.WriteHeader(http.StatusOK)
w.Write([]byte(“OK”))
})

srv := &http.Server{
Addr: “:8443”,
Handler: mux,
}

// HTTP/2の詳細な挙動を制御するためのコンフィグを設定
http2Server := &http2.Server{
// 同時ストリーム数を制限(デフォルトは無制限に近い値になることがあるため明示指定)
MaxConcurrentStreams: 100,

// 最大読込ヘッダーサイズ(HPACK展開後のサイズ)を制限
// 悪意ある巨大ヘッダーによるメモリ爆発を防ぐ防波堤となります
MaxDecoderHeaderTableSize: 4096,
}

// http/2プロトコルをサーバーに紐付け
err := http2.ConfigureServer(srv, http2Server)
if err != nil {
log.Fatalf(“HTTP/2の設定に失敗しました: %v”, err)
}

log.Println(“セキュアなHTTP/2サーバーをポート 8443 で起動します…”)
// 本番環境では必ず TLS (ListenAndServeTLS) を使用してください
if err := srv.ListenAndServeTLS(“server.crt”, “server.key”); err != nil {
log.Fatalf(“サーバーが異常終了しました: %v”, err)
}
}

—

5. デバッグとトラブルシューティングの実務Tips

「もしかして、うちのAPIも圧縮爆弾や変なHTTP/2アタックを受けているのでは?」と感じたとき、現場のシニアエンジニアが最初に行うべき調査ステップを伝授します。

1. メトリクスの観察ポイント

  • CPU使用率が低いのに、メモリ(RSS)が右肩上がりに急増して落ちる場合、CPUバウンドな処理やメモリリークではなく、「デコーダーやバッファ層での外部入力の肥大化」を疑ってください。

2. パケットキャプチャ(Wireshark / nghttp2)の活用

  • 本番の手前(Staging環境など)で検証を行う場合、`nghttp2` パッケージに含まれる `h2load` などのツールを使い、意図的に歪んだヘッダーを流して挙動を確認します。
  • Wiresharkでキャプチャする場合は、`http2.type == 1`(HEADERSフレーム)や `http2.type == 4`(SETTINGSフレーム)でフィルターし、`SETTINGS_HEADER_TABLE_SIZE` がどのようにやり取りされているかを確認します。

3. WAF(Web Application Firewall)やAPI Gatewayのアップデート

  • HPACK圧縮爆弾やRapid Reset(CVE-2023-44487)といった脆弱性は、OSやプログラミング言語のランタイムだけでなく、CloudflareやAWS CloudFront、AWS API Gatewayなどのエッジ層、あるいはEnvoyなどのプロキシのパッチ適用が最も効果的です。常に最新のパッチが当たっている状態を維持しましょう。

—

まとめ:便利さの裏側にある「境界線」を意識せよ

HTTP/2はWebの高速化において不可欠なプロトコルですが、「複雑になった分だけ、攻撃者にとっての隠れ場所が増えた」という側面も持っています。

HPACK圧縮爆弾は、プロトコルの仕様の隙間を突き、「正しく動くプログラム」の善意を逆手にとってリソースを奪う、非常に現代的で洗練された攻撃です。

「動くからよし」とするのではなく、「動的テーブルのサイズ制限は適切か」「同時ストリーム数は絞られているか」という境界線のガード固めを怠らないこと。それこそが、夜中のアラートに怯えない、強靭なインフラストラクチャを作り上げる唯一の道なのです。

コメント

タイトルとURLをコピーしました