Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How is it possible for a legitimate user to submit an invalid CSRF token in Rails?

Our error logs occasionally contain legitimate form submissions that cause ActionController::InvalidAuthenticityToken errors.

My hypothesis is that the CSRF token stored in the user's session cookie has changed at some point after the form was loaded but before it was submitted. This causes a mismatch between the POSTed token and the token in the cookie, leading to this error.

Given that a Rails session cookie expires only when the browsing session ends (ie when the web browser is closed), what are the ways in which this cookie (and the CSRF token it includes) can be changed without closing the browser?

We are using cookies to store session data, which is Rails' default behaviour.

like image 687
djb Avatar asked Jan 05 '17 10:01

djb


2 Answers

Here's what we know:

  • Legitimate form submissions that cause an InvalidAuthenticityToken exception have somehow lost their original CSRF token.
  • The token is kept in the session, which is kept in an encrypted cookie. So this error means their session cookie has changed since the form was generated.
  • The session cookie does not expire unless the user's browser window is closed.

I now believe there are two ways that invalid CSRF tokens can be submitted by legitimate users.

Note that these apply specifically to Rails 4.2. As I understand it, the "per-form CSRF tokens" feature in Rails 5 may mitigate them.

1. Session change in another tab

As per @crazymykl's answer, the user could open the form, then log out in another tab. This would cause the session cookie stored in the user's browser to change. When they came back to the original tab and submitted the form, the token from the form would not match the token in the session, and the error would pop.

2. Caching

As per this rails bug, Safari behaves oddly with caching under some circumstances. Telling it to reopen with the same windows as last time (via Safari > Preferences > General), opening the form and quitting Safari results in the form being redisplayed.

Submitting that cached form causes a CSRF error. As the opener of the bug concludes, it appears that Safari caches the page, but drops the session cookie. Hence the mismatch.

The solution to this problem is to set a Cache-Control header with the directive no-store. (Difference between no-cache and no-store explained here).

Further examples welcome :).

like image 184
djb Avatar answered Oct 12 '22 19:10

djb


Are you using Ajax form to submit the request? Does your page has multiple forms? If so, check your code whether correct csrf token is submitted along with the request. We had similar issue that page is rendered with one csrf token and we used this token to submit form one and we would have got another csrf token but the second was sending old csrf token that result in error. I am not sure how this is helpful but want to share similar problem faced by me

like image 20
Senthil Kumar Vaithiyanathan Avatar answered Oct 12 '22 20:10

Senthil Kumar Vaithiyanathan