Rails protect_from_forgery breaks login form if idle Rails protect_from_forgery breaks login form if idle ruby-on-rails ruby-on-rails

Rails protect_from_forgery breaks login form if idle


so what session is expiring?

The session in question is the Rails Session (see What are Sessions? in the Ruby on Rails Security Guide), which is a lightweight data-storage abstraction that persists arbitrary state across HTTP requests (in an encrypted cookie by default, other session-storage implementations can also be configured). Session state may include an authenticated user ID, but it can also be used for other purposes.

In this case, the session is not being used for user authentication, but to store a temporary 'authenticity token' as part of a Rails security feature that protects your website against Cross-Site Request Forgery (CSRF) attacks on POST (or other non-GET) requests.

The Rails API documentation describes this feature in more detail:

Controller actions are protected from Cross-Site Request Forgery (CSRF) attacks by including a token in the rendered HTML for your application. This token is stored as a random string in the session, to which an attacker does not have access. When a request reaches your application, Rails verifies the received token with the token in the session.

See also the Cross-Site Request Forgery section of the Ruby on Rails Security Guide for a more complete overview of what a CSRF attack is, and how session-based authenticity-token verification protects against it.

What other options are there to fix this?

  • Increase the duration of your Rails session expiration time (e.g., by increasing the duration of the expire_after option passed to your cookie_store initializer, or removing the option entirely to make the session never expire).

  • Instead of using session-cookie expiration to expire logged-in sessions, use Devise's :timeoutable module:

    devise :timeoutable, timeout_in: 15.minutes

Or is the solution we have used not as insecure as we think?

Configuring Rails to skip the verify_authenticity_token callback disables CSRF protection for that particular controller action, which makes the action vulnerable to CSRF attacks.

So to rephrase your question, is disabling CSRF protection only for the SessionsController#create action still insecure in any significant/meaningful sense? Though this depends on your application, in general yes, I believe so.

Consider this scenario:

A CSRF attack against SessionsController#create would allow an attacker to maliciously direct a victim's browser to login to a user account under the attacker's control. The next time the victim visited your website (e.g., when redirected by the attacker), their browser could still be logged in to the attacker-controlled account. The victim could then unknowingly submit sensitive personal data or perform sensitive actions on your website that could be read by the attacker.

While this scenario may be less obviously dangerous than what could happen if CSRF protection were disabled on other more sensitive/destructive controller actions, it still exposes enough of a potential user/data-privacy issue that I consider it to be insecure enough to recommend against disabling the default protection.


InvalidAuthenticityToken 

This error comes when "authenticity token" created while rendering form (login form) is already expired.

There are only two ways by which we can resolve this issue.

  • Configuring Rails to skip the verify_authenticity_token for controller#action - Insecure as already mentioned in the question
  • (Only left way)Auto reload the screen(here Login screen ) whenever such exception encountered.

Find the modified code for the same and add this in application_controller.rb to generalize the solution for whole application.

rescue_from ActionController::InvalidAuthenticityToken do  redirect_to root_urlend


Another option would be to include some JS on the page that would automatically reload a little while before your token expires (and therefore keep the auth token valid).

You would probably want to check to check for mouse/keyboard action before reloading in case the user is just in the process of filling out the form.

It seems that the biggest downside of this option would be the fact that the JS is decoupled from the actual amount of time the auth token is valid... So it could prove to be a maintenance issue if you change your timeout duration often.