CyberFix Note
セキュアコーディング

HTTPリクエストスマグリングの仕組みと対策。フロントとバックの境界解釈のズレを塞ぐ

対象の目安: Webアプリ/インフラ開発・実務

ソウ攻撃・脆弱性リサーチ担当
・ 約14分で読めます
HTTPリクエストスマグリングの仕組みと対策。フロントとバックの境界解釈のズレを塞ぐ

多くのWebサービスは、利用者からのリクエストをまずCDNやロードバランサやリバースプロキシで受け、その裏のバックエンドへ転送します。このフロントとバックが1本のTCP接続を使い回しながら、同じリクエストの「どこまでが1件目でどこからが2件目か」を別々に判断してしまうと、その隙間に細工した断片を潜り込ませられます。これがHTTPリクエストスマグリング、別名HTTPデシンク(desync)です。攻撃者が送った断片が、次にその接続を通る別の利用者のリクエストの先頭に連結され、本人の意図とは違うリクエストとして処理されてしまいます。

対象読者はWebアプリとインフラの開発と運用の担当者です。この記事はスマグリングが成立する機構を、リクエストの長さの決め方という一点から順に解き、CL.TEなどの古典型からHTTP/2ダウングレードで再燃した型まで整理し、最後に防御設計へ落とします。攻撃の再現ペイロードは示さず、概念の説明にとどめます。検証を行う場合は自組織が管理する、または明示的に許可を得た環境に限ってください。無許可で他者のシステムへ細工したリクエストを送る行為は、不正アクセス禁止法などに触れうるためです。

リクエストの長さをどう決めるか

HTTP/1.1でリクエストが1本のTCP接続に連続して流れるとき、受け手はヘッダの終わり(空行)のあとに続くバイト列のうち、どこまでが本文かを判断しなければなりません。この本文の長さの決め方が2通りあることが、スマグリングの出発点です。

ひとつはContent-Lengthで、本文のバイト数を数値で示します。もうひとつはTransfer-Encoding: chunkedで、本文をいくつかのチャンクに分け、各チャンクの先頭に長さを16進で書き、長さ0のチャンクで終端します。1件のリクエストにこの2つが同時に存在すると、本文の終わりが2通りに読めてしまい、受け手ごとに違う場所を境界と見なす余地が生まれます。

RFC 9112は、Transfer-EncodingContent-Lengthが両方あるメッセージについて、Transfer-EncodingContent-Lengthを上書きし、そうしたメッセージはリクエストスマグリングやレスポンス分割の試みを示す可能性があるためエラーとして扱うべきだとしています。転送を選ぶ中継者は、まず受け取ったContent-Lengthを取り除いてからTransfer-Encodingを処理して下流へ送る、と定めています。

RFC 9112はメッセージ本文長の規定で、Transfer-EncodingとContent-Lengthが併存する場合はTransfer-Encodingが優先され、そのようなメッセージはスマグリングやレスポンス分割の兆候としてエラー扱いすべきこと、中継者が転送するならContent-Lengthを除去してから処理すべきことを示しています。

問題は、実装がこの規定を必ず守るとは限らず、しかもフロントとバックで守り方が食い違うことです。片方はContent-Lengthを、もう片方はTransfer-Encodingを信じると、2つのサーバーが別の位置を境界と判断し、接続上にズレ(desync)が生じます。

境界がズレると何が混入するか

フロントとバックが同じ接続を使い回している点が、被害を広げる鍵です。フロントが「1件目のリクエストはここまで」と判断して転送したバイト列の中に、バックの解釈では「1件目の本文の外側」に当たる余りが含まれていると、その余りはバックの受信バッファに残ります。次にその接続を通ってきた別の利用者の正規リクエストが到着すると、残っていた余りがその先頭に連結され、1件のリクエストとして解釈されます。

つまり攻撃者は、自分のリクエストの末尾に「次のリクエストの先頭になる断片」を仕込むことで、被害者のリクエストの内容を書き換えたり、被害者のレスポンスを自分宛てに引き寄せたりできます。攻撃者が盗むのはネットワーク上の平文ではなく、サーバー内部でのリクエストの連結という処理の副産物である点が、通信を暗号化しても防げない理由です。

PortSwigger Web Security Academyは、リクエストスマグリングがフロントエンドとバックエンドが同一リクエストを異なって解釈することで発生し、これらの技法はHTTP/1でのみ成立すること、HTTP/2はフレームごとに長さを持つバイナリプロトコルでContent-Lengthを要さずTransfer-Encodingも用いないと説明しています。

古典的な3つの型

古典的なスマグリングは、CLとTEのどちらをフロントとバックが信じるかの組み合わせで整理されます。

フロントが見る長さバックが見る長さズレの生じ方
CL.TEContent-LengthTransfer-Encodingフロントが数えた本文の途中で、バックが長さ0のチャンクを見て早期に終端し、残りが次へ漏れる
TE.CLTransfer-EncodingContent-Lengthフロントがチャンクとして送った本文を、バックがContent-Lengthの長さで切り、余りが次へ漏れる
TE.TE両方がTE対応両方がTE対応ヘッダを難読化して片方だけにTransfer-Encodingを無視させ、実質CL.TEかTE.CLに落とす

CL.TEは、フロントがContent-Lengthを信じて本文全体を1件として転送する一方、バックがTransfer-Encoding: chunkedを信じ、本文の先頭にある長さ0のチャンクでリクエストが終わったと判断する型です。バックから見ると本文の残りは宙に浮き、次のリクエストの一部として扱われます。TE.CLは逆で、フロントがチャンク列として本文を読み切り、バックがContent-Lengthの数値ぶんだけ読んで残りを繰り越します。

TE.TEは、フロントとバックがどちらもTransfer-Encodingを扱えるが、ヘッダ名や値にわずかな崩し(余分な空白や大文字小文字、重複指定など)を入れることで、一方の実装にだけ「これは正しいTransfer-Encodingではない」と判断させ、無視させる型です。無視した側はContent-Lengthにフォールバックし、結果としてCL.TEやTE.CLと同じズレが起きます。

OWASP WSTGのHTTPリクエストスマグリングの検証項目は、CL.TEやTE.CL、TE.TEの分類と、フロントとバックのパーサ差を突く難読化の考え方を、テスト観点として示しています。

HTTP/2ダウングレードで再燃した型

HTTP/2はメッセージをフレームに分け、各フレームが自身の長さを持つバイナリプロトコルです。本文の長さはこの構造から一意に決まり、Content-Lengthは必須でなくTransfer-Encodingも使いません。したがってHTTP/2をエンドツーエンドで話す限り、長さ指定の矛盾という出発点そのものが存在しません。

再燃するのは、フロントが利用者とはHTTP/2で話し、バックへ転送する際にHTTP/1.1へ書き換える構成(ダウングレード)です。フロントはHTTP/2の組み込みの長さを使えますが、ダウングレードされたリクエストを受けるバックはその情報を持たず、Content-LengthTransfer-Encodingを頼るしかありません。ここで、HTTP/2側に含まれていた長さ情報とダウングレード後に生成されるヘッダが食い違うと、H2.CL(バックがContent-Lengthを見る)やH2.TE(バックがTransfer-Encodingを見る)というズレが生じます。

PortSwigger ResearchのHTTP/2研究は、フロントがHTTP/2を受けてバックへHTTP/1.1へダウングレードする構成でH2.TEやH2.CLのデシンクが成立しうること、対策としてHTTP/2をエンドツーエンドで話しダウングレードを避けることを挙げています。

James Kettleによる一連の研究、HTTP Desync Attacks(2019年)、HTTP/2 The Sequel(2021年)、Browser-Powered Desync Attacks(2022年)は、古典型に加えて境界のズレを起こす経路が複数あることを示しました。とりわけ2022年のBrowser-Powered Desync Attacksは、利用者のブラウザ自身に接続を汚染させるクライアントサイドdesync(CSD)や、サーバーが本文長を実質0として扱うCL.0を新たに命名し提示しています。共通する構図は変わらず、経路上の2つのHTTP実装がメッセージの区切りで合意できていないことが根にあります。

PortSwigger ResearchのHTTP Desync Attacksは、リクエストスマグリングが実務のインフラで広く成立しうることを実証し、タイミングを用いた検出手法や、キャッシュポイズニングと組み合わせた影響拡大の道筋を整理しています。

PortSwigger ResearchのBrowser-Powered Desync Attacks(2022年)は、利用者のブラウザに正当な形式のリクエストを送らせて自身の接続を汚染させるクライアントサイドdesync(CSD)や、サーバーがContent-Lengthを無視して本文長を0と扱うCL.0を新たに提示し、単一のサーバー構成でもデシンクが成立しうることを示しています。

何が起きるのか

境界のズレそのものは地味ですが、そこから派生する影響は広範です。機構の側から並べます。

  • フロントのセキュリティ制御の回避。WAFやアクセス制御はフロントが解釈したリクエスト単位で判定するため、バックだけが見る密輸部分にはフィルタが及ばず、禁止パスや攻撃ペイロードを裏へ通せます。
  • 他利用者のレスポンスの窃取や差し替え。密輸した断片が被害者のリクエストに連結されると、被害者宛てのレスポンスを攻撃者が受け取ったり、被害者に別の内容を返したりできます。
  • Webキャッシュポイズニング。汚染したレスポンスがキャッシュに載ると、以後その資産を要求した多数の利用者へ同じ汚染が配られます。
  • 資格情報やセッションの捕捉。被害者のリクエストに含まれるCookieや認証ヘッダを、攻撃者が観測できる場所へ書き出させることで、セッションを乗っ取る足場になります。

WAFの回避が起こりうる点は、WAFを入れているから安全という前提を崩します。WAFはあくまで多層の一枚であり、フロントとバックのパーサが合意していることを前提に効きます。WAFの守備範囲と限界は次の記事で整理しています。

あわせて読みたい

WAFの役割と限界、正しい使い方。シグネチャと振る舞い、回避される前提、根本対策との補完

密輸されたリクエストが内部ネットワーク宛てに向けられると、外部からは触れないはずの内部エンドポイントへの到達に使われることもあります。サーバーを起点に内部へリクエストを飛ばすという構図はSSRFと重なり、両者は組み合わさって影響を広げます。

あわせて読みたい

SSRFとは何か。サーバーを踏み台にする脆弱性の仕組みと許可リスト方式の防御を解説

防御の設計

対策は、境界のズレを起こさせないことと、曖昧なリクエストを経路のどこかで確実に潰すことの2軸です。

  1. 1

    可能ならHTTP/2をエンドツーエンドで使い、バックエンドへのHTTP/1.1ダウングレードをやめる。HTTP/2は本文長がフレーム構造から一意に決まり、CLとTEの矛盾という出発点が消える

  2. 2

    ダウングレードが避けられない場合、フロントで曖昧なリクエストを正規化してからバックへ渡す。CLとTEが両方あるリクエストは、RFCに沿ってTransfer-Encoding優先で処理しContent-Lengthを除去する、あるいはそもそも転送しない

  3. 3

    バックエンドは曖昧なリクエストを拒否し、疑わしい場合はその接続を再利用せず閉じる。1本の接続に残った余りが次へ繰り越されない

  4. 4

    フロントとバックのHTTP実装とパーサを揃え、Transfer-Encodingの難読化に対する解釈差をなくす。バージョンや設定の食い違いがそのままズレの温床になる

  5. 5

    接続の再利用(keep-alive)を前提にしている箇所を見直し、少なくともバックへの上流接続で曖昧なリクエストの後に接続を使い回さない設定にする

要点は、CLとTEが同時に存在するリクエストを経路のどこかで必ず不許可にすることです。RFC 9112はこうしたメッセージをエラーとして扱ってよいとしており、フロントで拒否するか、少なくとも一方を除去して正規化してから転送すれば、バックが別の解釈をする余地を消せます。

検出には、PortSwiggerのBurp拡張であるHTTP Request Smugglerや、レスポンスが返るまでの遅延の差を見るタイミング法が使われます。曖昧なリクエストを送った際にバックが次の入力を待って応答が遅れる挙動から、ズレの兆候を安全側に倒して観測する考え方です。いずれも自組織の管理下、または明示的な許可を得た対象に限って行ってください。

検証と確認の項目

自組織の構成を点検する際の確認項目を整理します。

  • 利用者からバックエンドまでの経路にHTTP/1.1ダウングレードが挟まっていないか把握しているか
  • フロント(CDNやLB、リバースプロキシ)とバックのHTTP実装とバージョンを把握し、揃えているか
  • CLとTEが両方あるリクエストを、フロントで拒否または正規化してから転送しているか
  • バックが曖昧なリクエストを拒否し、その接続を再利用せず閉じる設定になっているか
  • Transfer-Encodingの難読化(余分な空白や重複、大文字小文字)に対する解釈がフロントとバックで一致しているか
  • WAFやアクセス制御を、フロントとバックのパーサ合意を前提とした多層の一枚として位置づけているか
  • 上流接続のkeep-alive再利用が、曖昧なリクエストの後にも続く設定になっていないか
  • 検証は自組織の管理下または許可された環境に限り、再現ペイロードの取り扱いを統制しているか

リクエストスマグリングは、単体の脆弱性というより、経路上の実装差から生まれる構造的な弱点です。OWASP Top 10の枠組みでも、入力の検証や設定不備、コンポーネント間の信頼境界の設計と結びついて理解すると、優先順位がつけやすくなります。Webアプリの代表的リスクの全体像は次の記事で整理しています。

あわせて読みたい

OWASP Top 10とは。開発者が押さえるべきWebの代表的リスクと対策を一気に理解する

出典・参考

この記事をシェア

関連する記事