XMLHttpRequestを使ったCSRF(補足編)

XMLHttpRequestを使ったCSRF対策 - 葉っぱ日記を書いていて思ったけど、いまいちXHRを使ったCSRF(というかクロスオリジン通信)について理解されていないような感じだったので、ちょっと書いておきます。とりあえず日本語のリソース的には、HTTP access control | MDN が詳しくて、それを読めばだいたい事足りるんで、あとはCSRFに関連しそうな話題だけ。

Q. そもそも「クロスオリジン」って何?
スキーム、ホスト、ポートの3つの組み合わせが一致している場合を同一オリジン(same-origin)、いずれか一つでもことなる場合をクロスオリジン(cross-origin)と言います。つまり、XHRでドメインを超えて通信している場合は典型的なクロスオリジン通信となります。
Q. え? XMLHttpReuest って他のドメインにリクエストを発行できないんじゃ
いいえ。現在の主要なブラウザではクロスオリジンでの通信がサポートされています。IE8,9のXMLHttpRequestはクロスオリジン通信に対応していないけど、XDomainRequestという類似機能を使えばほぼ同等のことができます(setRequestHeaderとかできないけど)。
Q. XHRがクロスオリジン通信に対応しているかどうかJavaScript内で調べる方法は?
xhr.withCredentials !== undefined ならクロスドメイン通信に対応してます。
Q. サーバ側もクロスオリジン通信に対応していないと、XHRでのクロスオリジン通信はできないんじゃ。
半分正解、半分不正解です。サーバ側がクロスオリジン通信(要するにAccess-Control-Allow-Originヘッダを返すなど)に対応していない場合でも、XHRは一方的にリクエストを発行することはできます。ただし、responseTextなどを通じて応答を読むことはできません。
Q. XHRを使ってCSRFってどういうこと?
サーバ側の対応有無に関わらず、XHRはドメインを超えてリクエストを発行できますので、XHRによって罠サイトから一方的にPOSTを投げつけることで、対象サイトにCSRF脆弱性がある場合には攻撃が成功します。わざわざformを用意してsubmitする必要はありません。
Q. Origin リクエストヘッダって何?
XHRを使ってクロスオリジンのリクエストを発行する場合、発行元ページのオリジンがOrigin:ヘッダとしてリクエストに含まれるようになります(Google Chromeでは同一オリジンへのリクエストであってもPOST時にはOrigin:ヘッダがつきます)。Origin:ヘッダはsetRequestHeader等を使って偽装することはできません。
Q. preflightリクエストって何?
setRequestHeaderを使ってカスタムヘッダを付ける等の場合に、実際のリクエストに先立ち発行されるOPTIONSメソッドのことです。HTTP access control | MDN参照。

XMLHttpRequestがクロスドメイン通信に対応したことにより、攻撃者がより柔軟に送信データを組み立てることができるようになったため、それ以前に比べるとCSRFの与える影響範囲は少しだけ大きくなっています。(という話はそのうち。