aboutsummaryrefslogtreecommitdiffstats
path: root/actionpack/lib/action_controller/metal/request_forgery_protection.rb
diff options
context:
space:
mode:
authorSergey Nartimov <just.lest@gmail.com>2012-09-13 12:07:37 +0300
committerSergey Nartimov <just.lest@gmail.com>2012-09-13 12:07:37 +0300
commit95be790ece75710f2588558a6d5f40fd09543b97 (patch)
tree5dbcfe62d1337ed87c2afdc95f025aee22587a17 /actionpack/lib/action_controller/metal/request_forgery_protection.rb
parent616ba15f2cb89588ae3b0a55452f4059f2c118b1 (diff)
downloadrails-95be790ece75710f2588558a6d5f40fd09543b97.tar.gz
rails-95be790ece75710f2588558a6d5f40fd09543b97.tar.bz2
rails-95be790ece75710f2588558a6d5f40fd09543b97.zip
Implement :null_session CSRF protection method
It's further work on CSRF after 245941101b1ea00a9b1af613c20b0ee994a43946. The :null_session CSRF protection method provide an empty session during request processing but doesn't reset it completely (as :reset_session does).
Diffstat (limited to 'actionpack/lib/action_controller/metal/request_forgery_protection.rb')
-rw-r--r--actionpack/lib/action_controller/metal/request_forgery_protection.rb92
1 files changed, 70 insertions, 22 deletions
diff --git a/actionpack/lib/action_controller/metal/request_forgery_protection.rb b/actionpack/lib/action_controller/metal/request_forgery_protection.rb
index d5f1cbc1a8..81188ac8cc 100644
--- a/actionpack/lib/action_controller/metal/request_forgery_protection.rb
+++ b/actionpack/lib/action_controller/metal/request_forgery_protection.rb
@@ -49,10 +49,6 @@ 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?
@@ -78,12 +74,80 @@ module ActionController #:nodoc:
# Valid Options:
#
# * <tt>:only/:except</tt> - Passed to the <tt>before_filter</tt> call. Set which actions are verified.
- # * <tt>:with</tt> - Set the method to handle unverified request. Valid values: <tt>:exception</tt> and <tt>:reset_session</tt> (default).
+ # * <tt>:with</tt> - Set the method to handle unverified request.
+ #
+ # Valid unverified request handling methods are:
+ # * <tt>:exception</tt> - Raises ActionController::InvalidAuthenticityToken exception.
+ # * <tt>:reset_session</tt> - Resets the session.
+ # * <tt>:null_session</tt> - Provides an empty session during request but doesn't reset it completely. Used as default if <tt>:with</tt> option is not specified.
def protect_from_forgery(options = {})
+ include protection_method_module(options[:with] || :null_session)
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
+
+ private
+
+ def protection_method_module(name)
+ ActionController::RequestForgeryProtection::ProtectionMethods.const_get(name.to_s.classify)
+ rescue NameError
+ raise ArgumentError, 'Invalid request forgery protection method, use :null_session, :exception, or :reset_session'
+ end
+ end
+
+ module ProtectionMethods
+ module NullSession
+ protected
+
+ # This is the method that defines the application behavior when a request is found to be unverified.
+ def handle_unverified_request
+ request.session = NullSessionHash.new
+ request.env['action_dispatch.request.flash_hash'] = nil
+ request.env['rack.session.options'] = { skip: true }
+ request.env['action_dispatch.cookies'] = NullCookieJar.build(request)
+ end
+
+ class NullSessionHash < Rack::Session::Abstract::SessionHash #:nodoc:
+ def initialize
+ super(nil, nil)
+ @loaded = true
+ end
+
+ def exists?
+ true
+ end
+ end
+
+ class NullCookieJar < ActionDispatch::Cookies::CookieJar #:nodoc:
+ def self.build(request)
+ secret = request.env[ActionDispatch::Cookies::TOKEN_KEY]
+ host = request.host
+ secure = request.ssl?
+
+ new(secret, host, secure)
+ end
+
+ def write(*)
+ # nothing
+ end
+ end
+ end
+
+ module ResetSession
+ protected
+
+ def handle_unverified_request
+ reset_session
+ end
+ end
+
+ module Exception
+ protected
+
+ def handle_unverified_request
+ raise ActionController::InvalidAuthenticityToken
+ end
+ end
end
protected
@@ -95,22 +159,6 @@ module ActionController #:nodoc:
end
end
- # This is the method that defines the application behavior when a request is found to be unverified.
- # By default, \Rails uses <tt>request_forgery_protection_method</tt> when it finds an unverified request:
- #
- # * <tt>:reset_session</tt> - Resets the session.
- # * <tt>:exception</tt>: - Raises ActionController::InvalidAuthenticityToken exception.
- def handle_unverified_request
- 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:
#
# * is it a GET request? Gets should be safe and idempotent