Resource | Rails best practice security exceptions

Rails best practice security exceptions

Rails InvalidAuthenticityToken — around the edges

However, these security features can adversely affect real users and you may notice ActionController::InvalidAuthenticityToken exceptions appearing in your production runtime which can be challenging to reproduce.

The following scenarios have been identified that can create these exceptions in your production environment for ‘real’ users.

Stale authenticity token on a form

The User session has timed out. The user then logs back in and back pages to a form which contains an authenticity token for the old session. The browser will present the new session cookie when the form is submitted. Boom.

To resolve this the page form page should be reloaded from the server with the new session. Setting the following response headers will cause the browser to reload the form, and not use a cached version on disk.

# nginx config
add_header "Cache-Control" "max-age=0, no-cache, no-store, must-revalidate";
add_header "Pragma" "no-cache";
add_header "Expires" "-1";

User leaves a stale form open AND in a new tab logs out and logs in

In this case a new session is established, but the old page still exists and hence contains the old authenticity token (it doesn’t get a chance to reload using cache headers detailed above). You really don’t want someone submitting an old form on a new session so the authenticity token is kind of giving you a good side effect in this situation.

This is a UX question as well as a technical one and there are several options to prevent the user from getting an error.

Option 1 rescues the exception and does a GET request to refresh the page. The page will then load with a valid authenticity_token.

Option 2 uses the reset_session option to always brutally force them off the session. No logging occurs here which is unfortunate if you want monitor for real CSRF events. The page flow will then be caught by an authorize action in the request chain.

# option 1`

rescue_from ActionController::InvalidAuthenticityToken do |_exception|
  flash[:alert] = 'The session has been reset. Please try again.'
  redirect_back fallback_location: root_path

# option 2

protect_from_forgery with: :reset_session

There are other options that protect_from_forgery provides as well and you should read the full description of the API.

Message sent
Message could not be sent