protect_from_forgeryが何をやっているのか調べた
Rails3でCSRF対策としてApplicationControllerにデフォルト指定されるprotect_from_forgeryですが、実際のところ何をやっているのかわからなかったのでコードリーディングしてみたメモ。
処理の流れ
0. ApplicationControllerに下記の指定がされているところから始まる
class ApplicationController < ActionController::Base protect_from_forgery end
1. まずprepend_before_filterでverify_authenticity_tokenをbefore_filter郡に突っ込む
# File actionpack/lib/action_controller/metal/request_forgery_protection.rb, line 67 def protect_from_forgery(options = {}) self.request_forgery_protection_token ||= :authenticity_token prepend_before_filter :verify_authenticity_token, options end
2. verify_authenticity_tokenではverified_request?を呼ぶ
# File actionpack/lib/action_controller/metal/request_forgery_protection.rb, line 75 def verify_authenticity_token unless verified_request? logger.warn "WARNING: Can't verify CSRF token authenticity" if logger handle_unverified_request end end
3. verified_request?でリクエストデータを検証する
- protect_against_forgery?は設定ファイルでallow_forgery_protection = falseが指定されていないか検証
- GETリクエストか検証
- sessionに含まれるtokenとリクエストパラメータにrequest_forgery_protection_tokenが指定されていてマッチするか検証
- sessionに含まれるtokenとX-CSRF-Tokenヘッダのトークンが指定されていてマッチするか検証
の4項目を検証している。
# File actionpack/lib/action_controller/metal/request_forgery_protection.rb, line 93 def verified_request? !protect_against_forgery? || request.get? || form_authenticity_token == params[request_forgery_protection_token] || form_authenticity_token == request.headers['X-CSRF-Token'] end
4. 処理の分岐
verified_request?の検証で成功(どれか1つでもtrue)すれば何もしないでprotect_from_forgeryの処理は完了、
失敗(すべてfalse)だった場合はhandle_unverified_requestが呼ばれてさらにreset_sessionが呼ばれて
最終的にリクエストからsession情報がリセットされる。
# File actionpack/lib/action_controller/metal/request_forgery_protection.rb, line 84 def handle_unverified_request reset_session end
# File actionpack/lib/action_controller/metal/rack_delegation.rb, line 22 def reset_session @_request.reset_session end
# File actionpack/lib/action_dispatch/http/request.rb, line 209 def reset_session session.destroy if session && session.respond_to?(:destroy) self.session = {} @env['action_dispatch.request.flash_hash'] = nil end
まとめ
protect_from_forgeryはリクエストを検証して正しければ何もしない、正しくない場合はsessionをクリアする。