僕は発展途上技術者

Rails 3 での CSRF 対策 - エラー時に ActionController::InvalidAuthenticityToken error は表示されない

Rails 3.2.3 での CSRF 対策について調べていて、ちょっとはまったポイントがあったのでメモを残しておきます。

Rails はデフォルトで特に何もしなくても POST/PUT/DELETE のリクエストに対して authenticity_token という hidden のパラメーターを利用して CSRF 対策をおこなってくれる仕組みを持っている。(Ruby On Rails ピチカート街道 - Rails 2.0・その12(CSRFを勝手に防止) - などが参考になる)

しかしそれは Rails の form_for などのヘルパーを使った場合で、form タグを自分で書いた場合には、


<%= hidden_field_tag :authenticity_token, form_authenticity_token %>


のように手動で authenticity_token パラメーターを作って POST のパラメーターとして送る必要がある。

authenticity_token を送らない場合の挙動を確認したくて、適当に scaffold で model/view/controller 一式を作成し、form_for ヘルパーを使っている部分を以下のようにベタな html に書きなおし、あえて authenticity_token を送らないようにする。


<form accept-charset="UTF-8" action="/people" class="new_person" id="new_person" method="post">
<div class="field">
<label for="person_name">Name <input id="person_name" name="person[name]" size="30" type="text" />
</div>
<div class="actions">
<input name="commit" type="submit" value="Create Person" />
</div>
</form>


これで、POST のリクエストをおこなってもエラーとなって、新しいデータは作成されないと思ったのだが、予想に反してデータは作成されてしまう。

あれ、これだと CSRF 対策にならないじゃないか、と思いいろいろと試行錯誤。

ログをみると authenticity_token を送らない場合は

WARNING: Can't verify CSRF token authenticity

という WARNING のメッセージは確かに出ている。でも POST のリクエストは通ってしまう。

Rails 2.x を触っていたころの記憶では、authenticity_token を送ってない場合にエラーで悩まされた覚えがある。

で、しばしはまった後にわかったのは、authenticity_token を送らない場合は、POST のリクエストは通るのだが session がリセットされているということだった。

devise などで何らかのユーザー認証の仕組みをいれている場合に、POST/PUT/DELETE のアクションに対して、session に格納されているユーザー情報の値でもって認証をかけるのが普通だ。

これを scaffold でつくった簡単なフォームで擬似的に実現しようとすると、


class ApplicationController < ActionController::Base
protect_from_forgery

def authorize
unless session[:login]
redirect_to root_url, :notice => "not authorized"
end
end
end


のように session を確認する authorize メソッドを定義しておいて、PeopleController では、


before_filter :authorize, :only => [:create, :update, :destroy]


のように作成、変更、削除のアクションに対して authorize メソッドを before_filter でかける。

authenticity_token パラメーターを送らなかったり、値が違っている場合は session がリセットされるので、リクエスト自体は通っても authorize で蹴られてデータは作成されない。

「うーん、でもおかしいなあ、以前は確かにエラーではじかれた覚えがあるんだよなあ」と思って、もうちょっと調べていたら、Ruby on Rails Guides: Ruby On Rails Security Guide


If the security token doesn’t match what was expected, the session will be reset. Note: In Rails versions prior to 3.0.4, this raised an ActionController::InvalidAuthenticityToken error.



security token が期待された値とマッチしない場合はセッションがリセットされる。Rails 3.0.4 以前は ActionController::InvalidAuthenticityToken error を出していた。


とちゃんと書いてありました。

ドキュメントをきちんと読んでなかった僕が悪い。

プロフィール

株式会社まちクエスト代表、つくる社LLC代表。

Scratchで楽しく学ぶ アート&サイエンスRaspberry Piではじめる どきどきプログラミングを書きました。

Amazonから図書館検索 Libron、iPhoneアプリ ひらがなゲーム かなぶん を作っています。

Twitter @jishiha

最近のエントリー

アーカイブ