以前、CSRFについてのエントリを書きました。
上記エントリではCSRFの概念について書きましたが、もう少しつっこんで調べる必要が出てきました。調べたことを書いてゆきます。
基礎
application.rb(ないし適当なController)にprotect_from_forgeryメソッドを定義すれば、railsが自動的にCSRF対策をしてくれます。というか、デフォルトでapplication.rbに下記のように書いてあるので、特に何もせずともCSRF対策はバッチリなのです。
protect_from_forgery # :secret => '8ff3ed33f86a431662d8dfe255acdb4a'
railsは、get以外の動詞のリンクに、authenticity_tokenというパラメータを自動的に付け加えます。get以外の動詞の各アクションでparams[:authenticity_token]とsession[:csrf_id]を比較して、同値であればOKとしているようです。*1同値でなければActionController::InvalidAuthenticityTokenという例外がでます。
protect_form_forgeryのオプション
protect_from_forgery # :secret => '8ff3ed33f86a431662d8dfe255acdb4a'
上記のコードに書いてある:secretというオプションは、セッションをDBまたはメモリに格納しているときにだけ使います。デフォルトはセッションをクッキーに格納している*2ので、:secretはコメントアウトのままにしておきます。
特定のメソッドだけCSRFの対策をしたくない場合は、そのメソッドのあるコントローラでこんな風に定義します。
protect_from_forgery :except => ["create"]
:onlyも使えます。こうすると、createアクションの時のみprotect_from_forgeryが有効になります。
protect_from_forgery :only => ["create"]
railsのヘルパを使わずにajaxでdbをいじる方法
CSRFを詳しく調べた理由は、これがやりたかったから。
先ほども書きましたが、get以外の動詞(POST, PUT, DELETE)を使う時は、authenticity_tokenをセットする必要があります。なのでrailsのajax用のヘルパを使わずに、独自にjavascriptを書いてdbをいじる時は手動でauthenticity_tokenをいれてあげないといけません。また、PUTやDELETEはブラウザが対応していないので、特殊なやり方を取る必要があります。
authenticity_token
authenticity_tokenの値を入手するにはform_authenticity_tokenメソッドを使います。こんな感じでhiddenに入れてあげると良いと思います。
<%= hidden_field_tag "authenticity_token" form_authenticity_token %>
putやdeleteを使用したい場合
まず、railsは、下記のようにputを使うように定義すると
<% form_for :user, :method => "put"> # ... <% end %>
下記のようなHTMLを自動的に作成します。
<input hidden value="put" name="_method" />
そして、リクエストが来たときに、params[_method]の中を見てメソッドを判別しています。なので、javascriptの中で_methodを渡すように書いてあげれば良いです。
まとめ
まず、viewにauthenticity_tokenをセット。次にjavascriptでこんな感じに書きます(jquery使用してます)。ポイントは、
- _method
- authenticity_token
の二つをrailsに送信しているところ。
$(function(){ $("#hoge").click( function(){ $.ajax({ dataType: "text", data: { "_method": "put", "authenticity_token": $("#authenticity_token").val() "hoge": "hoge" }, type: "POST", cache: true, url: "http://www.example.com/", success: function(data) { // ... } }) } ) });