RailsのCSRF対策

Rails3でUnobtrusiveなJavaScriptを使うには、rails.jsをHTMLに含める必要があって、これをレイアウトに書くには、「javascript_include_tag :defaults」を入れておくのが基本。で、これで動いたかなと思ったら、今度は

ActionController::InvalidAuthenticityToken (ActionController::InvalidAuthenticit
yToken):

というエラーが出て、やっぱりdevise/registration#destroyが実行されない。認証トークンがないなら、いれればいいのかな、しかし明らかにログイン状態だし、なぜトークンが渡ってないんだとあれこれ悩む。10分ほどあれこれ読みあさっていたら、これはどうも、RailsCross Site Request Forgery対策関係の問題だと分かった。

Cookieトークンを保存している場合、例えばよそのドメインのサイトで、

<img src="http://www.webapp.com/project/1/destroy">

というリンクを踏まされたユーザーは、何も気づかずにプロジェクトを消されてしまう。webapp.comは、cookieからトークンを受け取るので正しいリクエストだと判断してしまう。リンクは掲示板に埋めてあってもいいし、onmouseoverに隠すというこもできて、けっこう何でもあり。これは潜在的には大きなセキュリティ上のリスクだ、ということで、Rails3では対策してある。それはGET以外のメソッドの場合に、トークンをサーバで生成してHTMLに含めるという方法で、これはapplication_controller.rbに、

protect_from_forgery

と入れておくことでできるようになる。

というCSRF対策は、実はRails2からあったらしい。ただ、Rails3では、この1行がデフォルトで含まれるようになったということか。

で、ぼくがRails2から持ってきたレイアウトのファイルには、このトークンを含める宣言が抜けていた。

<%= csrf_meta_tag %>

と書いておけば、生成されるHTMLのうちPOSTを受け付けるフォームには、

<input name="_method" type="hidden" value="put" /><input name="authenticity_token" type="hidden" value="r94Y6z9j1w/gdlijI8B57myugtvjLErbJSZ1u49vpdY=" />

トークンがhiddenで埋め込まれるようになる。なるほどなー。うーん、しかし、Rails3が作るトークンはみんな同じフォーマットなんだから、その気になれば、onmouseoverに埋め込んだJavaScriptならそのトークンを引っ張ってきた上で、リクエストを作ることもできそうに思えるけど。