僕は発展途上技術者

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 を出していた。


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

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

Mac OS X 10.8 Mountain Lion に Ruby on Rails 環境をセットアップする

開発環境をセットアップするのが面倒なので、OS をアップグレードするたび Time Capsule からイメージをまるごとコピーしてきて、秘伝のタレのように使っています。そのため、一から環境構築というのはめったにやらないのですが、今回、いつも使っている MacBook Air とは別に自宅用に Mac mini を買ったので、久々に Ruby on Rails の環境をセットアップしました。せっかくなので、まっさらの Mac OS X 10.8 Mountain Lion に Ruby on Rails をセットアップする手順例としてその手順を記録しておきます。

最近の Rails の環境構築は、gcc 入れた後に Ruby やら RVM やら Git やらを入れなくてはいけなくて、相当面倒です。しかし、RailsInstaller はそれらを一気にインストールしてくれます。

リンク先の DOWNLOAD the KIT をクリックしてインストールファイル(RailsInstaller-1.0.3-osx-10.7.app.tgz)をダウンロードします。

ダウンロードしたファイルをダブルクリックして展開します。

展開したファイルをダブルクリックすればインストールが開始されます...

のはずなのですが、ここで「"RailsInstaller-1.0.3-osx-10.7"は、開発元が未確認のため開けません。」というエラーが出てしまいます。

Mountain Lion からなのでしょうか?デフォルトの設定では、Mac App Store と確認済みの開発元からのアプリケーションでないとアプリケーションの実行が許可されていないので、

[システム環境設定] > [セキュリティとプライバシー]

を選んだあと、左下の鍵マークを外し、[一般] タブの [ダウンロードしたアプリケーションの実行許可:] で、[すべてのアプリケーションを許可] を選びます。

このあと、もう一度先ほど展開したファイルをダブルクリックすればインストールが開始されます。

必要な方は、この後 [セキュリティとプライバシー] の設定を元の、[Mac App Store と確認済みの開発元からのアプリケーションを許可] に戻しておきます。

RailsInstaller のインストールについては、指示通り進んでいけば特に問題はないと思います。

インストール後、


$ ruby -v
ruby 1.9.3p194 (2012-04-20 revision 35410) [x86_64-darwin11.4.0]
$ rails -v
Rails 3.2.8


のように ruby や rails の最新版がインストールされていれば成功です。

プロフィール

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

Raspberry Piではじめる どきどきプログラミングを書きました。

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

Twitter @jishiha

最近のエントリー

アーカイブ