第7章 エコーバック対策
HTTPレスポンスによるキャッシュ偽造攻撃対策

HTTPレスポンスによるキャッシュ偽造を許してしまうWebサーバの挙動

「HTTPレスポンスによるキャッシュ偽造(HTTP response splitting)」は、HTTPレスポンスヘッダの一部分をプログラムから出力するようにしているWebアプリケーションに干渉して偽のHTTPレスポンスを生じさせて、各種のキャッシュ(プロバイダのキャッシュサーバ、組織のプロキシサーバ、Webサイトのリバースプロキシサーバ、ブラウザのキャッシュ等)にそのレスポンスを記録させることによって、ひとりあるいは複数のユーザに悪意のWebページを閲覧させる攻撃である。

このような攻撃を受け入れてしまうのは、Webアプリケーションが入力パラメータの値に基づいてHTTPヘッダの部分も生成している場合である。入力パラメータの値をそのままHTTPヘッダ中にエコーバックするのみでは、この攻撃を許してしまう。

参考:

キャッシュ偽造に伴って想定される被害

この攻撃によって作られた悪意のキャッシュの内容は、次のようなことに悪用されるおそれがある。:

HTTPレスポンスによるキャッシュ偽造のメカニズム

Webアプリケーションは多くの場合、HTTPレスポンスのボディ部のみを書き出し、ヘッダ部はWebサーバソフトやアプリケーションエンジンが生成している。しかし、Cookieを発行する場合やリダイレクト先のURLを指定する場合など、Webアプリケーションが次のようなヘッダへの書き出しを行うようにしている場合がある。:

Webアプリケーションが書き出すこれらのヘッダに悪意のデータを混入し、本来は存在しないHTTPレスポンスをWebサーバに送出させるのがこの攻撃の要点である。

HTTPレスポンスによるキャッシュ偽造
図7-4:HTTPレスポンスによるキャッシュ偽造

HTTPレスポンスによるキャッシュ偽造は、次の要領で行われる。:

  1. 攻撃者は、パラメータに細工したHTTPリクエストをWebサーバに送りつける。ここで行われる細工はWebサーバが返すHTTPレスポンス内に改行コード(Cr Lf)が配置できることを悪用して予定外のHTTPレスポンスヘッダ行や空行を付け加え、現在のHTTPレスポンスが終了したかのように見せかけるものである。
  2. さらにその同じパラメータにデータを仕込んでHTTPレスポンスの続きを発生させる。終了したように見せかけたひとつ目のHTTPレスポンスの後ろにさらに別の独立したHTTPレスポンスが続いているかのような内容を作り上げる。
  3. このように捏造(ねつぞう)された第2のHTTPレスポンスに対応させるため、攻撃者は第1のHTTPリクエストを送り込んだのと同じTCP通信ストリームの続きに第2のHTTPリクエストを投入する。これにより、プロキシサーバが偽りの内容をキャッシュするように仕向けられる。

ここで仮に次のようなプログラム呼び出しのURLに対し、

http://site/redirect?location=/dir/another-page.html

次のようなLocation:ヘッダが書き出されるとする。:

Location: http://site/dir/another-page.html{Cr}{Lf}

例えば、このプログラムに対して攻撃者は、次のような文字列を入力パラメータに投入してくるのである。(ここでは複数行に分かれているがひとつにつながったURLとして与える。):

http://site/redirect?location=
/dummy%0AContent-Length%3A+0%0A%0AHTTP%2F1.1+200+OK%0ADate%3
A+Tue%2C+29+Aug+2006+05%3A23%3A48+GMT%0AContent-Type%3A+text
%2Fhtml%3B+charset%3DUTF-8%0AContent-Length%3A+69%0A%0A%3Cht
ml%3E%3Cbody%3E%3Ca+href%3D%22http%3A%2F%2Fbad-site%2Fbad-pa
ge%22%3Efake%3C%2Fa%3E%3C%2Fbody%3E%3C%2Fhtml%3E%0A

プログラムredirectが入力パラメータ中の改行コードに対処していなければ、次のようなHTTPレスポンスヘッダが出力される。:

Location: http://site/dummy{Cr}{Lf}
Content-Length: 0{Cr}{Lf}
{Cr}{Lf}
HTTP/1.1 200 OK{Cr}{Lf}
Date: Tue, 29 Aug 2006 05:23:48 GMT{Cr}{Lf}
Content-Type: text/html; charset=UTF-8{Cr}{Lf}
Content-Length: 69{Cr}{Lf}
{Cr}{Lf}
<html><body><a href="http://bad-site/bad-page">fake</a></body></html>

この最初の3行分で第1のHTTPレスポンスが終わり、4行目から新しいHTTPレスポンスが始まるかのように見える。

HTTPレスポンスによるキャッシュ偽造対策

「HTTPレスポンスによるキャッシュ偽造」ができないようにするためには、このようなWebアプリケーションは、入力データをHTTPレスポンスヘッダに書き出す際に、そこに含まれる改行コード(Cr および Lf)がそのまま出力されないようにする必要がある。

具体的な方法として、データ中から Cr や Lf を削除するやり方も考えられるが、HTTPヘッダ内に出力する値にURLエンコーディングを施すのが自然なやり方である。

PHPコード例
header("Location: " . urlencode($value));
Java Servletコード例
response.addHeader("Location: " + URLEncoder.encode(value));

URLエンコーディングを施すことにより、Cr は %0D で、Lf は %0A で表現されHTTPヘッダの中で特別な意味をもたなくなる。しかも、これらの値をURLとして扱ったり、再度Webアプリケーションがパラメータとして入力する場合も、URLエンコーディングをデコードして使うのが通常であるから、余分なロジックを必要としない。

なお、この対策は、プロキシサーバのキャッシュ偽造を根本から無くすものではない。別の攻撃手口によってプロキシキャッシュが汚染されることもありうる。

定期的にキャッシュをクリアする等、万一キャッシュが汚染された場合でも被害を小さく抑えるための運用を、プロキシサーバやキャッシュサーバ運用者が行うことが望ましい。

参考

PHP 4.4.2 およびそれ以降のバージョンでは、HTTPレスポンスヘッダを書き出すためのheader()関数に与えられた引数に改行文字が含まれていると実行時エラー(警告レベル)として検出するようになった。