Skip to main content

HTTPリクエストスマグリングをわかりやすく解説

著者
Headshot of Sam Sanoop

Sam Sanoop

prioritize vulns

2020年6月30日

0 分で読めます

HTTPリクエストスマグリングは、ここ数年で注目を集めている興味深いタイプの脆弱性です。この脆弱性を悪用すると、攻撃者はHTTP/1.1プロトコルの特定の機能を利用してセキュリティ保護を回避し、フィッシング攻撃を行ったり、自分以外のユーザーのリクエストから機密情報を取得したりする可能性があります。

また、ここ数か月の間に高額な報奨金が支払われたバグバウンティ報告が数多く公開されたことで、リクエストスマグリングはコミュニティで広く注目されています。Snykの調査によると、2020年にはこれまでに依存関係に関連するHTTPリクエストスマグリングが18件公開されています。

この脆弱性は、2005年にWatchfireが発表したホワイトペーパー「HTTPリクエストスマグリング」で初めて議論されました。その後、研究者のRegis Leroyが内容を発展させ、PortSwigger SecurityのJames KettleがBlackHat USA 2019でさらに取り上げたことで、メディアからも注目を集めました。

このブログ記事では、脆弱性の概要を解説し、オープンソースプロジェクトのメンテナーがプロジェクト内のHTTPリクエストスマグリングにパッチを適用するための修正方法を紹介します。

背景

新たな攻撃や脆弱性が注目を集め、メディアでも取り上げられるようになると、オープンソースライブラリは実際の攻撃の標的になることが少なくありません。適切な修正方法に関する知識が不足しているため、ライブラリ内の脆弱性の修正に数週間、あるいは数年かかることもあります。オープンソースコミュニティの安全性を高めるため、Snyk Security TeamはHTTPリクエストスマグリングなどの脆弱性を調査し、オープンソースの依存関係に潜む問題を発見するとともに、メンテナーが実行できる具体的な修正方法を提供しています。

その取り組みの一環として、Snykはオープンソースの依存関係エコシステムにおけるHTTPリクエストスマグリングの影響を調査し、数多くの脆弱性を発見しました。発見された脆弱性の詳細については、こちらをご覧ください。

Snykは、HTTPリクエストスマグリングの適切な修正方法を示す公開情報が不足していることを確認しました。また、メンテナーがこの脆弱性に適切に対処し、修正するまでに複数回のリリースが必要だったこともわかりました。このブログ記事を通じてそのギャップを埋め、この問題の解決方法を探しているメンテナーに詳しいアドバイスを提供します。

HTTPリクエストスマグリングの脆弱性とは

HTTPリクエストスマグリングを理解するには、まず次の項目を把握する必要があります。

Keep-Aliveとパイプライン処理

Keep-Aliveとパイプライン処理の概念は、当初RFC 2616で公開されました。

Keep-Aliveヘッダーは、持続的接続に関する情報を提供するホップバイホップヘッダーです。Webサーバーでは、「Connection」ヘッダー内でKeep-Aliveを指定することで、TCPソケット/接続を開いたままにできます。このヘッダーを使用すると、1つの接続で複数のリクエストとレスポンスを処理できるため、Webサーバーのオーバーヘッドを削減し、パフォーマンスを向上できます。現在、この機能はすべてのブラウザーとサーバーでサポートされています。

パイプライン処理も、RFC 2616で導入された機能です。Webサーバーは各リクエストを個別に処理するのではなく、先入れ先出しのストリームとして非同期に処理できます。そのため、前のレスポンスを待たずにリクエストを送信できます。

Content-LengthとTransfer-Encoding

HTTPリクエストにはメッセージ本文を含めることができます。リクエストに本文があることは、Content-LengthまたはTransfer-Encodingヘッダーフィールドで示されます。これらのヘッダーはメッセージのフレーミングに使用され、メッセージの終わりと次のメッセージの始まりをサーバーに伝えます。

RFC 7230のセクション3.3.2で定義されているContent-Lengthは、リクエストのエンティティ本文のサイズを示すHTTPヘッダーです。データ本文を含むHTTP POSTリクエストでよく使用されます。GETリクエストには通常本文がないため、Content-Lengthヘッダーを含めるべきではありません。

RFC 7230で定義されているTransfer-Encodingは、HTTP経由でバイナリデータを送信できるようにするために作られました。Transfer-Encodingには複数のディレクティブがありますが、この記事ではchunkedディレクティブに焦点を当てます。

chunkedディレクティブでは、データを複数のチャンクに分けて送信します。各チャンクの長さは16進数で指定し、その後に復帰と改行を続けます。chunkedディレクティブの終端は、0と空のシーケンスで示されます。chunkedリクエストの例を以下に示します。

POST / HTTP/1.1
Host: snyk.io
Content-Type: application/x-www-form-urlencoded
Transfer-Encoding: chunked

7(length of chunks)
foo=bar(series of chunks)
0(0 to terminate request followed by rn)
(rn)

リクエストスマグリング

最新のWebサーバースタックには、複数のWebサーバーに加えて、ロードバランサーやWSGIサーバーが含まれていることがよくあります。基本的な構成図は次のようになります。

ユーザーがフロントエンドのロードバランサーまたはプロキシを経由し、バックエンドのミドルウェア、WSGI、またはリバースプロキシを通じてアプリケーションに接続する様子を示す図。

HTTPリクエストスマグリングの脆弱性は、フロントエンドとバックエンドでHTTPリクエストの境界の解釈が異なり、両者の間に非同期状態が生じることで発生します。これは、Content-LengthとTransfer-Encodingの両方のヘッダーを処理する際に、フロントエンドやバックエンドのさまざまなライブラリがRFCの仕様から逸脱することが原因です。HTTPリクエスト本文はこれら2つのヘッダーに基づいてフレーミングされますが、仕様に従わない実装もあります。その結果、リクエストの一部が次のリクエストに追加されたり、紛れ込んだりします。これにより、スマグリングされたリクエストへのレスポンスが別のユーザーに返される可能性があります。

この脆弱性は、フィッシング攻撃、キャッシュポイズニング、クロスサイトスクリプティング(XSS)などに悪用される可能性があります。この脆弱性の悪用に関する詳細は、James Kettleが昨年のBlackHat USA 2019で「HTTP Desync Attacks: Request Smuggling Reborn」と題して発表しました。この記事では、最も一般的な2つのリクエストスマグリング手法に焦点を当てます。

  • CL:CL:Content-Length二重指定攻撃の手法

  • CL:TE:Content-LengthとTransfer-

    Encodingを使った攻撃手法

CL:CL:Content-Length二重指定攻撃の手法

RFC 7230のセクション3.3.3#4には、次のように記載されています。

「Transfer-Encodingがなく、値の異なる複数のContent-Lengthヘッダーフィールド、または無効な値の単一のContent-Lengthヘッダーフィールドを含むメッセージを受信した場合、メッセージのフレーミングは無効であり、受信者は回復不能なエラーとして扱わなければならない」

しかし、現在多くのミドルウェアやWebサーバーは、本文を含むGETリクエストを寛容に処理します。さらに、RFC 7231のセクション4.3#4.3.1には、「GETリクエストメッセージ内のペイロードには定義されたセマンティクスがない。GETリクエストでペイロード本文を送信すると、既存の実装によってはリクエストが拒否される可能性がある」と記載されています。これは、多くのサーバーやプロキシがこの動作をサポートしている可能性を示しています。場合によっては、これがリクエストスマグリング攻撃につながることがあります。この記事では、この攻撃の一つのパターンを解説します。

GET / HTTP/1.1
Content-Length: 43
Content-Length: 0
Host: snyk.io

GET /reqsmuggle HTTP/1.1
Host: snyk.io

2つのContent-Lengthヘッダーが指定されている場合、どちらのヘッダーを優先するかについてフロントエンドとバックエンドの実装に違いがあると、スマグリング攻撃が発生する可能性があります。

攻撃者、ユーザー、ロードバランサー、リバースプロキシ、アプリケーションサーバー間のHTTPリクエストスマグリングを示す図。

上記のHTTPリクエストでは、フロントエンドにプロキシまたはロードバランサーがあるターゲットに対し、2つのContent-Lengthヘッダーを含むリクエストを送信しています。プロキシは最初のContent-Lengthを優先し、GETリクエストに本文があってはならず、Content-Lengthヘッダーが2つ指定されているにもかかわらず、スマグリングされたリクエストをリクエスト本文の一部として扱います。バックエンドで処理される際には、最初のContent-Lengthヘッダーは無視され、2つ目のContent-Lengthヘッダーが優先されます。2つ目のContent-Lengthは0に設定されているため、バックエンドはリクエスト本文がないものとして扱い、/reqsmuggleリクエストを別のパイプラインリクエストとして処理します。その結果、このスマグリングされたリクエストへのレスポンスが別のユーザーに届く可能性があります。

HTTP/1.1 200 OK
Content-Length: 11

hello worldHTTP/1.1 404 Not Found
Connection: Close
Content-Length: 0

CL:TE:Content-LengthとTransfer-Encodingを使った攻撃手法

PortSwigger LabsのJames Kettleが発見したこのよく知られた手法では、1つのHTTPリクエストにContent-LengthヘッダーとTransfer-Encodingヘッダーを含め、それらの扱いを操作します。これにより、フロントエンドのプロキシとバックエンドサーバーで優先されるヘッダーが異なり、スマグリングされたリクエストの処理に違いが生じます。この脆弱性を示すリクエストの例を以下に示します。

POST /login HTTP/1.1
Host: snyk.io
Content-Type: application/x-www-form-urlencoded
Content-Length: 62
Transfer-Encoding: chunked

16
login=xxx&password=xxx
0

GET /404 HTTP/1.1
X-Foo: bar

このリクエストがフロントエンドのプロキシで処理されると、Content-Lengthヘッダーが優先され、Transfer-Encodingヘッダーよりも先に処理されます。しかし、バックエンドサーバーではTransfer-Encodingヘッダーが優先されます。そのため、バックエンドはリクエストを処理し、文字「0」の位置で終端します。その結果、404 GETリクエストは独立したパイプラインリクエストとして扱われます。

この問題は、フロントエンドがTransfer-EncodingヘッダーよりContent-Lengthを優先することで発生します。RFC 7230のセクション3.3.3#3によると、Content-LengthとTransfer-Encodingの両方を含むメッセージが受け入れられた場合、Transfer-EncodingヘッダーがContent-Lengthを上書きします。フロントエンドはこの仕様に従っていません。

HTTP Content-Length攻撃を示す図。攻撃者がチャンク形式のPOSTリクエストにGETリクエストを紛れ込ませ、ロードバランサーとバックエンドを経由してアプリケーションに送信します。

フロントエンドがTransfer-Encodingヘッダーを優先する場合でも、有効なものではなく、不正なTransfer-Encodingヘッダーを挿入することで、この制限を回避できる可能性があります。以下に例を示します。

Transfer-Encoding : chunked
tRANSFER-ENCODING: chunked
Transfer-Encoding: chunk

多くのHTTPライブラリはTransfer-Encodingヘッダーのさまざまな形式を許容し、クライアントの利便性を高めるために正規化します。そのため、バックエンドサーバーがどのような形式のTransfer-Encodingヘッダーを正規化するかを把握すれば、不正なTransfer-Encodingヘッダーをフロントエンド経由で送り込み、CL:TEスマグリング攻撃を実行できる可能性があります。

上記の例は脆弱性を示すものにすぎません。実際の攻撃では、フィッシング攻撃を仕掛けたり、被害者のリクエストからデータを盗み、攻撃者が管理するサーバーに送信したりするために、スマグリングされたリクエストを細工できます。

対象範囲と誤解

今回の調査でSnykは、攻撃ベクトルが複雑であることから、HTTPリクエストスマグリングについての報告内容に誤解が生じている事例を数多く確認しました。研究者やメンテナーは、修正が自分の管理するライブラリの対象範囲に含まれるのか、それともライブラリが使用する依存関係側で対処すべきなのかを、必ずしも理解していませんでした。

さらに明確化が必要だった課題には、次のようなものがあります。

  1. プロキシ、ロードバランサーなど、フロントエンドに分類されるコンポーネントは、この脆弱性の影響を特に受けやすいものです。しかし、リクエストスマグリング攻撃を成功させるには、脆弱なフロントエンドとバックエンドの両方を悪用する必要があります。

  2. 修正の責任は、フロントエンドのメンテナーだけでなく、バックエンドのメンテナーにもあります。不正なヘッダーを正規化するのはバックエンドとして妥当な動作であり、ユーザーの誤りに対してより寛容であるべきだ、また、リクエストを正規化せずに転送するプロキシこそが本当の問題だ、という意見もあるでしょう。しかし、依存関係のエコシステムは広大で、バックエンドとフロントエンドの両方に該当するライブラリも数多く存在するため、双方が問題の修正に取り組むのが理想的です。

  3. ミドルウェア、リバースプロキシ機能をサポートするWebサーバー、WSGIサーバーや高性能サーバーは、バックエンドの条件に該当すると考えられます。不正なリクエストを処理するだけで、パイプライン処理をサポートしないWebサーバーは該当しません。

  4. 複数のリクエストを受信したサーバーが応答し、それぞれが別個のレスポンスとして表示されることから、セキュリティ報告ではHTTPリクエストスマグリングの問題としてメンテナーに報告されることがよくあります。多くのサーバーはKeep-Aliveとパイプライン処理をサポートしていますが、それだけでHTTPリクエストスマグリングの脆弱性に該当するわけではありません。NGINXで報告されたCVE-2020-12440がその例です。

修正方法

HTTPリクエストスマグリングの問題は、現行のHTTP仕様からの逸脱や、複数のライブラリがRFC 7230に準拠していないことが原因で発生します。また、この脆弱性への対処は、フロントエンドとバックエンドのどちらのプロジェクトを保守しているかによって難易度が異なります。そこでSnykは、現在オープンソースプロジェクトで実装されている対策を調査し、以下のように分類しました。わかりやすくするため、すべての対策について、その理由と防止できるスマグリング攻撃の種類をあわせて紹介します。理想的には、多層防御のアプローチとして、以下の対策をすべて適用してください。

Transfer-EncodingをContent-Lengthより優先する対策:この対策により、CL:TE攻撃とTE:CL攻撃を防止できます。対象範囲: フロントエンド、バックエンド詳細:Transfer-Encoding: chunkedヘッダーとContent-Lengthの両方を含むリクエストを受信した場合は、Content-LengthよりTransfer-Encodingヘッダーを優先する必要があります。これはRFC 7230のセクション3.3.3 #3に記載されています。

Content-LengthとTransfer-Encodingの両方を含むリクエスト、およびContent-Lengthヘッダーが重複するリクエストを拒否する対策: この対策により、CL:CL攻撃、CL:TE攻撃、TE:CL攻撃を防止できます。対象範囲: バックエンド、フロントエンド、バックエンド、上流ライブラリ詳細:これは「Transfer-EncodingをContent-Lengthより優先する」対策よりも優れた選択肢と考えられます。Node.jsなどの実行環境では、両方のヘッダーを含むリクエストにHTTP 400レスポンスを返すことで、リクエストスマグリングへの対策を実施しています。

Snykは、この手法がスマグリングの問題を防ぐ理想的な修正方法だと考えています。ただし、修正の適用方法には注意が必要です。特に、フロントエンドなど多くのパッケージが依存する低レベルのHTTPライブラリや上流エンジンに修正を適用する場合は、慎重に検討してください。 

HTTPリクエストスマグリングの問題に対処する際は、RFC 7230 #3.3.3に従う必要があります。RFC 7230のセクション3.3.3 #3には、次のように記載されています。

「Transfer-EncodingとContent-Lengthの両方のヘッダーフィールドを含むメッセージを受信した場合、Transfer-EncodingがContent-Lengthより優先される。このようなメッセージは、リクエストスマグリング(セクション9.5)またはレスポンス分割(セクション9.4)を試みている可能性があるため、エラーとして扱うべきである。送信者は、このようなメッセージを下流に転送する前に、受信したContent-Lengthフィールドを必ず削除しなければならない。」

ただし、これはRFC 2616 4.4 #3の記述とは異なります。

「Transfer-EncodingヘッダーフィールドとContent-Lengthヘッダーフィールドの両方を含むメッセージを受信した場合、後者は必ず無視しなければならない。」

RFC 2616 4.4 #3は廃止され、RFC 7230に置き換えられている点に注意してください。修正を実装する際はRFC 7230を考慮する必要があります。したがって、Content-LengthとTransfer-Encodingの両方のヘッダーを含むHTTPリクエストを処理する場合、フロントエンドはContent-Lengthヘッダーを削除してから下流のバックエンドにリクエストを転送するのが正しい動作です。HTTP 400レスポンスを返すべきではありません。複数のContent-Lengthヘッダーを含むリクエストについても、RFC 7230 #3.3.2の記述に従い、同様に処理する必要があります。

RFC 7230のセクション3.3.2に記載されているとおり、異なる長さの値を持つ複数のContent-Lengthヘッダーを含むHTTPリクエストを受信した場合は、HTTP 400レスポンスを返すか、重複したフィールド値を有効なContent-Lengthフィールド1つに置き換えることで対処できます。Snykは、低レベルのHTTPライブラリでは、複数のヘッダーを有効なヘッダー1つに置き換える方法を推奨しています。

不正な形式のTransfer-Encodingヘッダーを拒否し、複数のTE値を正しく処理する対策:この対策により、TE:TE攻撃を防止できます。対象範囲: フロントエンド、バックエンド詳細:フロントエンドとバックエンドの両方がTransfer-Encodingヘッダーを優先する場合、攻撃者が2つのTransfer-Encodingヘッダーを挿入することでスマグリング攻撃が可能になることがあります。一方のヘッダーはフロントエンドに無視されてバックエンドで処理され、もう一方はその逆になります。そのため、以下のような形式のヘッダーは拒否する必要があります。

Transfer-Encodingヘッダーのバリエーションと、サーバーが各バリエーションをどのように正規化または処理するかをまとめた表。

ただし、攻撃者が、ここに記載されていない不正な形式の「chunked」エンコーディングヘッダーを見つける可能性は残ります。そのため、この対策だけに頼るべきではありません。Content-LengthとTransfer-Encodingの両方を含むリクエストを拒否するとともに、不正な形式のヘッダーも拒否する方法がより適切です。

最後に、「chunked」の値が「gzip」や「deflate」など、ほかのTransfer-Encodingの値とともに指定されている場合も見落とさないようにしてください。Transfer-EncodingはContent-Lengthより優先する必要があります。この脆弱性の発生例として、CVE-2019-16786が挙げられます。次のTransfer-Encoding値を含むHTTPリクエストが送信された場合、「chunked」の値を正しく識別し、優先する必要があります。

Transfer-Encoding: gzip, chunked

Transfer-Encodingヘッダーには、複数の値をカンマ区切りで指定できます。しかし、フロントエンドが「gzip」の値だけを認識してContent-Lengthヘッダーを優先し、バックエンドがリクエストをchunkedとして処理した結果、CL:TE攻撃が発生する場合があります。複数の値が指定されている場合は正しく確認し、「chunked」が指定されていればTransfer-Encodingを優先することをSnykは推奨します。また、複数のTransfer-Encodingヘッダーが指定されている場合、「chunked」の値は、ほかのTransfer-Encoding値の後に最後の値としてのみ指定できます。これはRFC 7230 セクション3.3.1に記載されています。

まとめ

HTTPリクエストスマグリングは、理解や修正が難しい脆弱性です。このブログ記事が、保守担当者の皆さんによる効率的なパッチの作成と、オープンソースプロジェクトのセキュリティ強化に役立てば幸いです。

なお、この記事ではHTTPリクエストスマグリングの手法を2つ取り上げているにすぎず、さまざまな攻撃シナリオについて詳しく解説しているわけではありません。セキュリティ研究者のZeddYuによる次の記事では、HTTPリクエストスマグリングを幅広く取り上げ、各攻撃手法について詳しく解説しています。Zeddy Yu: HTTPスマグリングを1つの記事で理解する。

Capture the Flagを始めよう

オンデマンドのバーチャル入門ワークショップを見て、Capture the Flagの課題の解き方を学びましょう。

カテゴリー:

続きを読む

Blog

フロンティアモデルは脆弱性を発見した。攻撃者だけがエクスプロイトチェーンを見つけた。

静的解析で欠陥は見つかりましたが、ライブ攻撃テストで侵害につながる連鎖を実証できたのは唯一でした。Evo COS、Claude Security、Claude Code Securityを比較します。

feature insights context
Blog

自律型攻撃はすでに始まっている。防御もそのスピードに追いつかなければならない。

自律型攻撃者によって、防御に使える時間は短くなっています。継続的な検出、修復、検証、予防で、セキュリティチームが攻撃に歩調を合わせる方法をご紹介します。

Blog

脆弱性のバックログはもはや技術的負債ではなく、攻撃対象領域です

増え続ける脆弱性のバックログは、単なる技術的負債ではありません。攻撃対象領域そのものです。古いリスクの前提、攻撃の自動化、そして連鎖する検出結果によって、なぜ新たなアプローチが求められているのかを解説します。