RailsのCSRF対策
Rails3でUnobtrusiveなJavaScriptを使うには、rails.jsをHTMLに含める必要があって、これをレイアウトに書くには、「javascript_include_tag :defaults」を入れておくのが基本。で、これで動いたかなと思ったら、今度は
ActionController::InvalidAuthenticityToken (ActionController::InvalidAuthenticit yToken):
というエラーが出て、やっぱりdevise/registration#destroyが実行されない。認証トークンがないなら、いれればいいのかな、しかし明らかにログイン状態だし、なぜトークンが渡ってないんだとあれこれ悩む。10分ほどあれこれ読みあさっていたら、これはどうも、RailsのCross 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ならそのトークンを引っ張ってきた上で、リクエストを作ることもできそうに思えるけど。