ラベル セキュリティ の投稿を表示しています。 すべての投稿を表示
ラベル セキュリティ の投稿を表示しています。 すべての投稿を表示

2012年12月31日月曜日

AjaxベースのWebアプリにおけるCSRF対策

私の苦手なセキュリティまわりの話。
CSRFについて勉強、備忘録をここに残します。

言うまでもありませんが、CSRFは
攻撃者が作成した攻撃用サイトを利用者に利用させ、
利用者の意図しないリクエストを送信させる手法をさします。

CSRF対策として、form送信ベースのWebアプリでは、
ワンタイムトークンを利用するのがメジャーなようです。
ワンタイムトークンは危険なCookieには格納せず、
ページ内のhidden等に格納するのが安全とされます。
また、ブラウザのキャッシュ機構によるトークン再利用を避けるために、
リクエストにはGETを使わずPOSTにするなど。

ワンタイムトークンの欠点としては、
(1)タブ等による画面分裂に対応しづらい、
(2)ブラウザの戻る進むによってトークンが再利用されてしまう、
(3)Ajaxなど平行して複数リクエストが送信される場合の対応が難しい、
などがあります。

(1)(3)は複数のトークンをページに与えることで対応できなくはないのですが、
画面分裂、同時リクエスト数をクライアント側で制御する必要があり、実装が複雑になりそう。

そこで調べたところ、
(3)のAjaxで送信する場合に限れば、
ワンタイムトークン以外の簡単な方法でCSRF対策を補えそうです。

HTTPヘッダのX-requested-withに値が設定されているか否かを
サーバ側でチェックすることにより、
CSRF対策ができます。(ただしクロスドメイン間通信を許容していない限り)

X-requested-withの値によって、
リクエストがAjaxのものなのか、Ajax以外のものかを判別することができるからです。
jQueryなどでAjax関数を実行すると、
X-requested-withに"XMLHttpRequest"を自動的につけるらしい。

同一生成元ポリシーにより
攻撃サイトからのAjax(XMLHttpRequest)リクエストが原則不可能なため、
Ajaxによるリクエストであるということが分かれば正規のリクエストと判別できます。

CSRFで使われるであろう、ブラウザのURL欄直接入力や、
javascriptのlocation.hrefやsubmit()からのリクエストには
X-requested-withが無い(かは確証がないが、少なくとも"XMLHttpRequest"以外である)
のと、ブラウザ側でX-requested-withを設定することができないことから、
攻撃サイトがAjaxリクエストを偽装することも不可。
上記理由によりJSONハイジャックも拒絶することができます。

ただし、XMLHttpRequestのLevel2ではクロスドメイン間でAjaxリクエストが可能なため、
当対策だけでは不十分になるので注意。

また、CSRFは防げてもセッションハイジャックなどの他のセキュリティ問題への
解決にはならないので、可能な限りトークンなどの他対策と組み合わせたほうがよいと思います。

SEがこんなこと言ってはいけないのですが、
セキュリティって考えるの面倒ですね・・・