[Rails] protect_from_forgery の明示的なストラテジー指定が必須に - デフォルト動作の非推奨化
背景: 一貫性のない動作が引き起こす混乱
Railsでは、CSRF(Cross-Site Request Forgery)攻撃からアプリケーションを保護するため、protect_from_forgeryメソッドを使用します。しかし、このメソッドには長年の一貫性の問題がありました。
protect_from_forgeryを引数なしで呼び出すと、デフォルトで:null_sessionストラテジーが使用されます。一方、config.action_controller.default_protect_from_forgeryを有効にした場合は:exceptionストラテジーが使用されます。この不一致により、開発者が意図しない動作に遭遇するケースがありました。
#56537で報告されたこの問題に対処するため、Rails 8.2ではprotect_from_forgeryを戦略指定なしで呼び出すことが非推奨となりました。
技術的な変更内容
新しい設定オプションの追加
新たにconfig.action_controller.default_protect_from_forgery_with設定が導入されました。この設定により、アプリケーション全体でのデフォルトストラテジーを統一的に管理できます。
if respond_to?(:action_controller)
action_controller.forgery_protection_verification_strategy = :header_only
action_controller.default_protect_from_forgery_with = :exception
end
Rails 8.2では、この設定のデフォルト値は:exceptionに変更されます。ただし、後方互換性のため、現在のバージョンでは:null_sessionのままです。
クラスレベルでの設定管理
RequestForgeryProtectionモジュールに新しいクラス変数とデリゲートメソッドが追加されました。
singleton_class.delegate :default_protect_from_forgery_with, :default_protect_from_forgery_with=, to: :config
delegate :default_protect_from_forgery_with, :default_protect_from_forgery_with=, to: :config
self.default_protect_from_forgery_with = :null_session
protect_from_forgeryメソッドの動作変更
:withオプションが指定されていない場合、非推奨警告が表示されるようになりました。
strategy = if options.key?(:with)
options[:with]
else
if default_protect_from_forgery_with == :null_session
ActiveSupport::Deprecation.warn(<<~MSG.squish, ActionController.deprecator)
Calling `protect_from_forgery` without specifying a strategy is deprecated.
Please specify a strategy with the `:with` option.
MSG
end
default_protect_from_forgery_with
end
self.forgery_protection_strategy = protection_method_class(strategy)
移行方法
Rails 8.2の新しい動作を早期に採用する
Rails 8.2の動作を今すぐ採用したい場合は、以下の設定を追加します。
config.action_controller.default_protect_from_forgery_with = :exception
非推奨警告を解消しつつ現在の動作を維持する
現在の動作を維持したい場合は、明示的に:withオプションを指定します。
class ApplicationController < ActionController::Base
protect_from_forgery with: :null_session
end
ストラテジーの違い
Railsは3つの主要なCSRF保護ストラテジーを提供しています。
:exception
CSRF検証が失敗した場合、ActionController::InvalidCrossOriginRequest例外を発生させます。これが今後の推奨デフォルトです。
:reset_session
CSRF検証が失敗した場合、セッション全体をリセットします。
:null_session
CSRF検証が失敗した場合、リクエスト中は空のセッションを提供しますが、完全にリセットはしません。現在のデフォルトですが、将来的には:exceptionに変更されます。
テストへの影響
この変更により、既存のテストコードも更新が必要になる場合があります。
変更前:
PrependTrueController = Class.new(PrependProtectForgeryBaseController) do
protect_from_forgery prepend: true
end
変更後:
PrependTrueController = Class.new(PrependProtectForgeryBaseController) do
protect_from_forgery prepend: true, with: :null_session
end
まとめ
この変更により、RailsアプリケーションにおけるCSRF保護の動作がより明示的で予測可能になります。:exceptionストラテジーへの移行は、セキュリティの観点からも望ましい変更といえるでしょう。既存のアプリケーションは、非推奨警告に従って適切にストラテジーを明示することで、スムーズに移行できます。