[アップデート] ALB および CLB に HTTP Desync 緩和モードが機能追加されました

私の力不足により実機検証の内容はありません。アップデート内容およびその背景について触り程度にまとめました。
2020.08.18

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

本日のアップデートで ALB および CLB が HTTP Desync 緩和モードをサポートするようになりました。

何がうれしいのか

HTTP Desync 攻撃とは

このアップデートの何が嬉しいのかを理解するには、まず HTTP Desync 攻撃 について知る必要があります。

近年では Web アプリケーションでは CDN やプロキシをフロントエンドに配置し、バックエンドのサーバーにリクエストを転送するような構成を一般的にとられているかと思います。まず大前提として HTTP Desync 攻撃は、このようなフロントエンド、バックエンド構成において成り立ちます。

Web リクエストは RFC 7230 準拠の標準的なメカニズムに従って解釈されますが、非準拠のリクエストに対しては、その解釈が異なる場合があります。HTTP Desync 攻撃 はこのフロントエンドとバックエンド間で異なる解釈の違いを利用し、悪意のあるリクエストを送り込む攻撃です。

HTTP Desync を利用した攻撃例として、以下のようなものがあげられます。

  • アクセス制御のバイパス
  • クロスサイトスクリプティング(XSS)
  • Web Chache Poisoning
  • Web Chache Deception
  • 他ユーザーのリクエストを窃取
  • オープンリダイレクト

この仕組みについて詳細なレポートは black hat USA 2019 「HTTP Desync Attacks: Smashing into the Cell Next Door」を参照いただくのが良いでしょう。また、同著者による DEF CON 27 でのセッション動画も公開されていますので視聴をおすすめします。

HTTP request smuggling

HTTP Desync 攻撃の歴史は古く、2005 年に HTTP request smuggling(HRS) という攻撃手法が報告されています

例えば以下のような一般的なフロントエンド、バックエンドのアーキテクチャを例にします。フロントエンドは複数の異なるユーザーからの HTTP リクエストを受けとり、単一の TCP/TLS コネクションでルーティングを行います。この仕組みを実現するには、各メッセージの終端場所の解釈がフロントエンドとバックエンドとで一致している必要があります。

(引用元:PortSwigger:HTTP request smuggling

もしフロントエンドとバックエンドで終端の解釈が異なる場合、攻撃者は曖昧なメッセージ(オレンジ色)を送信することでバックエンドに異なる HTTP リクエストである、と誤った解釈をさせることが可能となります。これにより、曖昧なメッセージが、健全なユーザーのメッセージ(緑色)の前に仕込まれた形でバックエンドに渡すことが可能となります。

(引用元:PortSwigger:HTTP request smuggling

以下は簡単な攻撃例です。リクエストには Content-LengthTransfer-Encoding 2つのヘッダーが存在します。RFC 2616 ではこのような場合、後者を無視すると定義されています。

もし、メッセージが Transfer-Encoding ヘッダフィールドと Content-Length ヘッダフィールドとを一緒に送ってきたならば、後者は無視しなければならない。

しかし暗黙的に Transfer-EncodingContent-Length が混在していてもリクエストを処理できるため、多くのサーバーでこのリクエストを拒否することはありません。仮にフロントエンドがチャンクエンコーディングをサポートしない(0 を終端として認識しない)場合、G のメッセージは後者の HTTP リクエストとしてバックエンドに渡されます。

(引用元:PortSwigger:HTTP request smuggling

この例はただの G ですが、ここに悪意のある処理を埋め込み、誰かのリクエストに乗せることが出来るということです。

Transfer-Encoding の難読化

先ほどの例ではフロントエンド、バックエンドの双方がチャンクエンコーディングをサポートしてない場合に機能しません。しかし、Transfer-Encoding ヘッダーの記述を曖昧にすることで解釈の違いを生み、どちらか一方で Transfer-Encoding ヘッダーを処理させることができます。

曖昧なメッセージは以下のように「不要な文字の追加」「空白」「タブ」など、非常に簡単に作り出すことができます。

(引用元:HTTP DESYNC ATTACKS -SMASHING INTO THE CELL NEXT DOOR-

防御方法

これらの攻撃を防ぐ方法はいくつかあります。

  • バックエンドとの通信に HTTP/2 のみを利用する
    • HTTP/2 ではチャンクエンコーディングに対応していません
  • バックエンド接続の再利用を完全に無効にする
    • Keep-Alive はオーバーヘッド削減のため HTTP1.1 で広く使われています
  • 曖昧なリクエストをフロントエンドで正規化する
    • 一部の CDN では対応しているようです
  • 曖昧なリクエストをフロントエンドで拒否する
    • おまたせしました!今回のアップデートがこの機能追加です!

Desync 緩和モード

ロードバランサーは HTTP Desync Guardian と呼ばれる AWS オープンソースライブラリを使用し、脅威レベルに基づいてすべてのリクエストを分類します。脅威レベルは以下の 4 分類です。

分類 説明
Compliant(準拠) リクエストは RFC 7230 に準拠している
Acceptable(許容可能) リクエストは RFC 7230 に準拠していないが、既知のセキュリティ上の脅威はない
Ambiguous(曖昧) リクエストは RFC 7230 に準拠していないが、さまざまな Web サーバーやプロキシが異なる処理をする可能性があるため、リスクがある
Severe(重大) リクエストは高いセキュリティリスクをもたらす。ロードバランサーはリクエストをブロックし、400 のレスポンスをクライアントに提供し、クライアントの接続を閉じる

次に、Desync 緩和モードの設定に基づき、リクエストの許可、拒否を判断してリクエストを流します。

緩和モードは以下の 3 つです。モードと分類に基づきリクエストの処理が異なります。デフォルトおよび既存のロードバランサーは 防御的 になっています。

分類 モニタリング 防御的 最も厳格
Compliant 許可 許可 許可
Acceptable 許可 許可 ブロック
Ambiguous 許可 許可(*) ブロック
Severe 許可 ブロック ブロック

(*) リクエストはルーティングされますが、クライアントとターゲットの接続は閉じられます

確認

今回は ALB の設定箇所の確認のみです。実際の攻撃的なリクエストを送ってみるなどの検証は、この分野に強いメンバーにお任せします。。

ALB の例ですが、[属性] の設定項目を確認すると、[Desync 緩和モード] が追加されているのがわかります。

アクセスログのフィールド追加

また、今回の機能追加に伴いアクセスログのフィールドclassification および classification_reason が追加されています。

classification

RFC 7230 に準拠していないリクエストがあった場合に Acceptable, Ambiguous, Severe の値が記録されます。RFC 7230 に準拠している場合は - になります。

classification_reason

RFC 7230 に準拠していないリクエストがあった場合に分類の理由のいずれかのコードが記録されます。RFC 7230 に準拠している場合は - になります。

さいごに

恥ずかしながら今回のアップデート内容ではじめて HTTP Desync 攻撃というものを知りました。調べてみると仕組みとしては凄く単純であることが理解できました。

基本的にフロントエンド、バックエンド構成、且つ、フロントエンド/バックエンドで曖昧なリクエストに対する解釈の違いがなければ発生しうるものではありません。しかし、クラウド利用が普及する昨今、CDN や LB などのプロキシはマネージドサービスに委ねられているため、フロントエンドとバックエンドの解釈の違いが起きやすい状況にある、とも言えそうです。

「最も厳格」に設定される際には、従来のリクエストがブロックされる可能性が大いにありますので、くれぐれも検証等で十分に確認してご利用ください。

以上!大阪オフィスの丸毛(@marumo1981)でした!