From 245941101b1ea00a9b1af613c20b0ee994a43946 Mon Sep 17 00:00:00 2001 From: Sergey Nartimov Date: Fri, 9 Mar 2012 19:33:06 +0300 Subject: configure how unverified request will be handled can be configured using `:with` option in `protect_from_forgery` method or `request_forgery_protection_method` config option possible values: - :reset_session (default) - :exception new applications are generated with: protect_from_forgery :with => :exception --- .../metal/request_forgery_protection.rb | 20 ++++++++++++++++++-- .../controller/request_forgery_protection_test.rb | 4 ++-- .../app/controllers/application_controller.rb | 4 +++- railties/test/application/configuration_test.rb | 18 ++++++++++++++---- 4 files changed, 37 insertions(+), 9 deletions(-) diff --git a/actionpack/lib/action_controller/metal/request_forgery_protection.rb b/actionpack/lib/action_controller/metal/request_forgery_protection.rb index afa9243f02..3081c14c09 100644 --- a/actionpack/lib/action_controller/metal/request_forgery_protection.rb +++ b/actionpack/lib/action_controller/metal/request_forgery_protection.rb @@ -37,6 +37,10 @@ module ActionController #:nodoc: config_accessor :request_forgery_protection_token self.request_forgery_protection_token ||= :authenticity_token + # Controls how unverified request will be handled + config_accessor :request_forgery_protection_method + self.request_forgery_protection_method ||= :reset_session + # Controls whether request forgery protection is turned on or not. Turned off by default only in test mode. config_accessor :allow_forgery_protection self.allow_forgery_protection = true if allow_forgery_protection.nil? @@ -64,8 +68,10 @@ module ActionController #:nodoc: # Valid Options: # # * :only/:except - Passed to the before_filter call. Set which actions are verified. + # * :with - Set the method to handle unverified request. Valid values: :exception and :reset_session (default). def protect_from_forgery(options = {}) self.request_forgery_protection_token ||= :authenticity_token + self.request_forgery_protection_method = options.delete(:with) if options.key?(:with) prepend_before_filter :verify_authenticity_token, options end end @@ -80,9 +86,19 @@ module ActionController #:nodoc: end # This is the method that defines the application behavior when a request is found to be unverified. - # By default, \Rails resets the session when it finds an unverified request. + # By default, \Rails uses request_forgery_protection_method when it finds an unverified request: + # + # * :reset_session - Resets the session. + # * :exception: - Raises ActionController::InvalidAuthenticityToken exception. def handle_unverified_request - reset_session + case request_forgery_protection_method + when :exception + raise ActionController::InvalidAuthenticityToken + when :reset_session + reset_session + else + raise ArgumentError, 'Invalid request forgery protection method, use :exception or :reset_session' + end end # Returns true or false if a request is verified. Checks: diff --git a/actionpack/test/controller/request_forgery_protection_test.rb b/actionpack/test/controller/request_forgery_protection_test.rb index 64ed7f667f..ef795dad89 100644 --- a/actionpack/test/controller/request_forgery_protection_test.rb +++ b/actionpack/test/controller/request_forgery_protection_test.rb @@ -43,7 +43,7 @@ class RequestForgeryProtectionController < ActionController::Base protect_from_forgery :only => %w(index meta) end -class RequestForgeryProtectionControllerUsingOldBehaviour < ActionController::Base +class RequestForgeryProtectionControllerUsingException < ActionController::Base include RequestForgeryProtectionActions protect_from_forgery :only => %w(index meta) @@ -215,7 +215,7 @@ class RequestForgeryProtectionControllerTest < ActionController::TestCase end end -class RequestForgeryProtectionControllerUsingOldBehaviourTest < ActionController::TestCase +class RequestForgeryProtectionControllerUsingExceptionTest < ActionController::TestCase include RequestForgeryProtectionTests def assert_blocked assert_raises(ActionController::InvalidAuthenticityToken) do diff --git a/railties/lib/rails/generators/rails/app/templates/app/controllers/application_controller.rb b/railties/lib/rails/generators/rails/app/templates/app/controllers/application_controller.rb index e8065d9505..b3d6adad2a 100644 --- a/railties/lib/rails/generators/rails/app/templates/app/controllers/application_controller.rb +++ b/railties/lib/rails/generators/rails/app/templates/app/controllers/application_controller.rb @@ -1,3 +1,5 @@ class ApplicationController < ActionController::Base - protect_from_forgery + # prevent CSRF attacks by raising an exception, + # if your application has an API, you'll probably need to use :reset_session + protect_from_forgery :with => :exception end diff --git a/railties/test/application/configuration_test.rb b/railties/test/application/configuration_test.rb index c9310aff87..ac5ac2b93e 100644 --- a/railties/test/application/configuration_test.rb +++ b/railties/test/application/configuration_test.rb @@ -275,19 +275,23 @@ module ApplicationTests require "#{app_path}/config/environment" + token = "cf50faa3fe97702ca1ae" + PostsController.any_instance.stubs(:form_authenticity_token).returns(token) + params = {:authenticity_token => token} + get "/posts/1" assert_match /patch/, last_response.body - patch "/posts/1" + patch "/posts/1", params assert_match /update/, last_response.body - patch "/posts/1" + patch "/posts/1", params assert_equal 200, last_response.status - put "/posts/1" + put "/posts/1", params assert_match /update/, last_response.body - put "/posts/1" + put "/posts/1", params assert_equal 200, last_response.status end @@ -528,6 +532,12 @@ module ApplicationTests end RUBY + app_file 'app/controllers/application_controller.rb', <<-RUBY + class ApplicationController < ActionController::Base + protect_from_forgery :with => :reset_session # as we are testing API here + end + RUBY + app_file 'app/controllers/posts_controller.rb', <<-RUBY class PostsController < ApplicationController def create -- cgit v1.2.3