CSRFとReferer改竄

seasurfersというMLに参加している。このMLの名称(seasurfers)は、CSRF(Cross Site Request Forgery)をひねった?もののようで、CSRFをはじめとしたWeb APセキュリティが主な話題のようだ。

ここで最近取り上げられていたのは、RefererCSRFを防止する対策の問題だ。この対策は、攻撃者が自身のRefererを制御(改竄)するのはたやすいが、第三者Refererを制御するのは不可能であることを利用したもの。Refererチェックは技術的に簡単にできるため、RefererCSRF対策を行なっているサイトも多いかもしれない。

MLで話題になっていたのは、Flashの欠陥(bugtraqに7月に投稿された記事交差点の猫 - リファラーを 偽装するのよ Flashで)を利用すると、攻撃者が被害者を任意のサイトにアクセスさせ、その際のRefererを含むHTTPヘッダを攻撃者が自由に制御できるということ。この欠陥を突かれると、RefererによるCSRF対策を迂回することができる。そのためMLでは、「RefererCSRF対策に使うべきではない」という主張がなされていた。

私自身も実際に、macromediaAdobe)のFlash Professional 8(30日無料トライアル版)をダウンロードして、FlashのSWFファイルを作成し、上記の攻撃が可能であることを確認した。

まずは、私が行なった実験の概要を以下に書く。

SWFファイルには、以下のようなActionScriptを記述する(そもそものActionScriptの書き方は、このへんのサイトを参考にした)。

var req:LoadVars=new LoadVars(); 
req.addRequestHeader("Foo","Bar"); 
req.addRequestHeader("Referer","http://hoge.com/"); 
req.decode("a=b&c=d"); 
req.send("http://site_b/test.php", "_blank", "POST"); 
stop();

このSWFファイルと、このSWFを読み込むobject要素を含むHTMLファイルを、適当なWebサーバ(サイトA)のディレクトリに置く。

サイトAに置いたobject要素を含むHTMLに、ブラウザ(IE6)でアクセスすると、ブラウザにSWFがロードされ実行される。SWF内のActionScriptは、新しいブラウザウィンドウ(_blank)をオープンし、そのウィンドウにサイトBのページ(http://site_b/test.php)を表示するよう動作する。

Paros ProxyでサイトBへのリクエストを見ると、LoadVars.sendメソッドで指定したようにPOSTになっており、ボディーにはLoadVars.decode指定のパラメータが付いている。また、LoadVars.addRequestHeaderで指定したHTTPヘッダ Foo、Refererが付いている(IE6SP2では、Refererヘッダが二行現れることもない)。また、サイトBであらかじめ仕込んでおいたcookieが、このリクエストでもサイトBに送られた(この事実は、CSRF攻撃を仕掛けるものには朗報だ)。

このサイトBへのリクエスト送信に際して、crossdomain.xmlファイルを、サイトBに置いておく必要は無い。無くても、Flashやブラウザが、何かの警告を発することは無い(サイトBからのレスポンスをActionScriptで受け取る場合には、この辺の事情が異なる)。

なお、ActionScriptのLoadVars.sendメソッドでは、サイトBからのレスポンスをActionScriptで受け取ることはできない。イメージだが、このsendメソッドはXMLHttpRequest的なものではなく、単なるリンクだと理解した。ただし、リンク先へのリクエストHTTPヘッダをリンク元が制御できる点で、通常のHTMLでのリンクとは異なっている。

前置きが長くなったが、私がこの問題について思うことを以下に書く。

最初に思ったことは、今回の問題はあくまでFlashの欠陥であって(Adobeが欠陥だと認めるかは判らないが、私は欠陥だと思う)、Refererを使ったCSRF対策そのものの欠陥ではないということ。ただし、現時点でFlashにこのような未修正の欠陥があるため、CSRF対策にRefererを使うのは控えるのが賢明だろう。

それから、私が思うのは、この欠陥の責任の一部は、ブラウザが負っているということだ。というのは、サイトBにリクエストを発しているのは、最終的にはブラウザ(IE6)であるからだ。Flashは、Flash AP(SWF)からのリクエスト要求を、ブラウザに渡している。そして、ブラウザはFlashからの要求をそのまま全て受け入れて、サイトBにリクエストを送っている。このブラウザの挙動(Flashからの要求を丸呑みする)は、安易過ぎるのではないか。

つまり、ブラウザがリクエストを送る以上、Refererなどの基本的なリクエストヘッダは、ブラウザが責任を持って決めるべきではないだろうか。そうであれば、今回のような問題は生じなかっただろうし、もしもFlash以外のプラグインに同様の欠陥があっても、それが問題になることは無い。逆の言い方をすれば、仮にFlashの欠陥が修正されたとしても、ブラウザがダメなのであれば、別のプラグインの欠陥によって、同じ問題が引き起こされるかもしれない。

ここでの話は、ブラウザがプラグインにどこまでの権限・機能を与えるかというセキュリティモデルに関わっている。プラグインは、エンドユーザが信頼して、ブラウザに組み込むものだという前提があるため、ブラウザは非常に多くの権限・機能をプラグインに与えている。しかし、今回の件では、ブラウザ自身がその責任において実行すべきことにまで、プラグインという外部の介入を許したことが、問題の根本にあるように思える。

最後に、気になるのは、この脆弱性Adobeやブラウザ製作者が「脆弱性」「欠陥」として認識しているかということだ。この問題がBugtraqに報告されたのは、今年の7月。だいぶ時間は経っている。「製品仕様です」と開き直っていると困るのだが。