Rails raises an InvalidAuthenticityToken
when the CSRF token doesn't match. But, from reading the source, I can't figure out how this actually happens. I start by acking the tree for that class:
$ ack --ignore-dir=test InvalidAuthenticityToken
actionpack/lib/action_controller/metal/request_forgery_protection.rb
4: class InvalidAuthenticityToken < ActionControllerError #:nodoc:
17: # which will check the token and raise an ActionController::InvalidAuthenticityToken
actionpack/lib/action_dispatch/middleware/show_exceptions.rb
22: 'ActionController::InvalidAuthenticityToken' => :unprocessable_entity
Only two hits, ignoring the comment. The first one is the class definition:
class InvalidAuthenticityToken < ActionControllerError #:nodoc:
end
The second one is translating the exception into an HTTP status code. CSRF protection gets enabled by calling protect_from_forgery
in the controller, so let's look at that:
def protect_from_forgery(options = {})
self.request_forgery_protection_token ||= :authenticity_token
before_filter :verify_authentic开发者_运维问答ity_token, options
end
It adds a filter:
def verify_authenticity_token
verified_request? || handle_unverified_request
end
Which calls this when verification fails:
def handle_unverified_request
reset_session
end
So how is InvalidAuthenticityToken
actually raised?
The behavior was changed fairly recently but the documentation has yet to be updated. The new approach being used is to presume the session has been hijacked, and therefore to clear the session. Assuming your session contains the all-important authentication information for this request (like the fact you're logged in as alice
) and your controller assures the user is authenticated for this action, your request will be redirected to a login page (or however you choose to handle a non logged-in user). However, for requests which are not authenticated, like a sign-up form, the request would go through using an empty session.
It seems this commit also goes on to close a CSRF vulnerability, but I didn't read into the details of that.
To obtain the old behavior, you would simply define this method:
def handle_unverified_request
raise(ActionController::InvalidAuthenticityToken)
end
You can read more about CSRF and other Rails security issues at the Ruby on Rails Security Guide.
verify_authenticity_token used to be defined as
verified_request? || raise(ActionController::InvalidAuthenticityToken)
but as you noted, it now calls handle_unverified_request
, which in turn calls reset_session
I don't think Rails actually throws that exception anymore.
http://weblog.rubyonrails.org/2011/2/8/csrf-protection-bypass-in-ruby-on-rails states
After applying this patch failed CSRF requests will no longer generate HTTP 500 errors, instead the session will be reset. Users can override this behaviour by overriding handle_unverified_request in their own controllers.
https://github.com/rails/rails/commit/66ce3843d32e9f2ac3b1da20067af53019bbb034
精彩评论