RailsのCSRF周りのコードリーディングをしました。

コードリーディングをする前に使い方を復習すると、protect_from_forgeryメソッドをコントローラで呼んで、csrf_meta_tagsのヘルパーでmetaタグを埋め込めばOKです。

ActionController::RequestForgeryProtection::ClassMethods#protect_from_forgeryは以下のようになっています。

verify_authenticity_tokenはCSRFトークンを検証するbefore_actionのメソッド、クラス変数を操作しているのはCSRFのストラテジやトークンのパラメータ名になります。

ビューから呼び出せるcsrf_meta_tagsメソッドはActionView::Helpers::CsrfHelperのメソッドです。

#request_forgery_protection_tokenは:authenticity_tokenがセットされます。

#form_authenticity_tokenは#masked_authenticity_tokenを呼び出します。

real_csrf_tokenはSecureRandomを使ってランダムなトークンを生成してセッションに格納します。one_time_padを生成して、csrf_tokenに対してXORを取って暗号化し、暗号化したトークンとone_time_padを連結したものをbase64エンコードしたものを返します。実体のsession['_csrf_token']はセッションごとに生成しつつ、ユーザに対して発行されるCSRFトークンはリクエスト毎に変わってくるため、SSLのBREACH攻撃を防ぐことができます。

CSRFトークン検証用の#verify_authenticity_tokenは#valid_authenticity_token?を呼び出します。上記で行ったマスキングの逆の操作を行い、CSRFトークンの検証を行います。

verifyに失敗した場合は#handle_unverified_request経由でforgery_protection_strategy.new(self).handle_unverified_request が呼ばれます。#protect_from_forgeryのwithオプションに:exceptionを指定した場合は、forgery_protection_strategyはActionController::RequestForgeryProtection::ProtectionMethods::Exceptionになります。この場合、#handle_unverified_requestの処理はActionController::InvalidAuthenticityTokenのraiseになります。