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

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

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

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

こうした攻撃を受け入れてしまうのは、入力パラメータの値をHTTPレスポンスヘッダの中に組み入れる際、そこに含まれる改行コード(Cr および Lf)をそのまま出力してしまうWebアプリケーションである。

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

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

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

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

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

  1. 攻撃者は、パラメータに細工したHTTPリクエストをWebサーバに送りつける。ここで行われる細工はWebサーバが返すHTTPレスポンス内に改行コード(Cr Lf)が配置できることを悪用して予定外のHTTPレスポンスヘッダ行や空行を付け加え、現在のHTTPレスポンスが終了したかのように見せかけるものである。
  2. さらにその同じパラメータにデータを仕込んでHTTPレスポンスの続きを発生させる。終了したように見せかけたひとつ目のHTTPレスポンスの後ろにさらに別の独立したHTTPレスポンスが続いているかのような内容を作り上げる。
  3. こうしてねつ造された第二のHTTPレスポンスに対応させるため、攻撃者は第一のHTTPリクエストを送り込んだのと同じTCP通信ストリームの続きに第二の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行分で第一の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()関数に与えられた引数に改行文字が含まれていると実行時エラー(警告レベル)として検出するようになった。