クッキーを使わずに毎度認証情報をヘッダーに付与してもらう形式のステートレスなRestfulAPIを提供する場合は、
skip_before_action :verify_authenticity_token
のようにCSRF Tokenの検証をスキップしたいです。
今回、開発中にあやまって一部のAPIでスキップをしわすれていたのですが、その事に自動テストで気がつく事ができなかったため、
そういった誤りをしている場合に自動テストで検出されるようにしました。
CSRF Tokenの検証はallow_forgery_protection
フラグによって制御される
Railsのプロジェクトを生成した再に、test環境ではallow_forgery_protection
フラグがfalseになっています:
# config/environments/test.rb config.action_controller.allow_forgery_protection = false
こちらのallow_forgery_protection
は
def protect_against_forgery? # :doc: allow_forgery_protection end
で利用されており、
def verified_request? # :doc: !protect_against_forgery? || request.get? || request.head? || (valid_request_origin? && any_authenticity_token_valid?) end
GETとHEADのリクエスト以外で、CSRF Tokenの検証を実施するかどうかの分岐につかわれています。
def verify_authenticity_token # :doc: mark_for_same_origin_verification! if !verified_request? if logger && log_warning_on_csrf_failure if valid_request_origin? logger.warn "Can't verify CSRF token authenticity." else logger.warn "HTTP Origin header (#{request.origin}) didn't match request.base_url (#{request.base_url})" end end handle_unverified_request end end
そのため、CSRF Tokenの検証は、test環境ではcontrollerでskipしているしていないにかかわらず実施されない事になります。
CSRF Tokenの検証をテストケースの前後で有効にする
そのため、今回はテストケースの前後で、自動的に、allow_forgery_protection
をtrueにしてケースを実行し、falseに戻す処理を追加するモジュールを定義しました:
# spec/support/api_request_example_group.rb # frozen_string_literal: true module ApiRequestExampleGroup extend ActiveSupport::Concern included do around do |ex| ActionController::Base.allow_forgery_protection = true ex.call ensure ActionController::Base.allow_forgery_protection = false end end end
このモジュールを、APIのテストケースのspecファイルでincludeする事によって、APIのテストに関してはCSRFの検証が実施されるようになり、 適切にskipできていない場合にはCSRF Tokenの検証失敗の例外があがり、ミスに気がつけるようになりました。