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_authenticity_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?
Rails CSRF TokenThe server generates these tokens, links them to the user session, and stores them in the database. This token is then injected into any form presented to the client as a hidden field. When the client correctly submits the form for validation, it passes the token back to the server.
When the user submits the form, Rails looks for the authenticity_token , compares it to the one stored in the session, and if they match the request is allowed to continue. Since the authenticity token is stored in the session, the client cannot know its value.
A CSRF token is a secure random token (e.g., synchronizer token or challenge token) that is used to prevent CSRF attacks. The token needs to be unique per user session and should be of large random value to make it difficult to guess. A CSRF secure application assigns a unique CSRF token for every user session.
Enable CSRF Protection With REST API If our project requires CSRF protection, we can send the CSRF token with a cookie by using CookieCsrfTokenRepository in a custom WebSecurityConfigurerAdapter. After restarting the app, our requests receive HTTP errors, which means that CSRF protection is enabled.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With