8

I had a look into CSRF recently. The recommended mitigation strategy is to implement the Synchronizer Token Pattern.

When you look at the details, the question comes up how often these tokens need to be changed. Again, the general recommendation is that per-session tokens are sufficient. You can go for per-request tokens if you need extra security (or if you have a scenario where leaking CSRF tokens is an issue).

While verifying how big sites implement CSRF protection, I had a look at github.com. What boggles me is that I cannot understand why they implemented their approach as they did.

It occurs to me that they use per-request tokens (even more, unique per form tokens). However, old tokens are not invalidated after they have been used. I will give an example.

Let us consider the github profiles page: https://github.com/settings/profile.

The response to a GET request contains three forms with an authenticity_token as hidden input parameter. If I remember correctly, github is built based on Rails. So this fits, as the name matches the default name of Rails CSRF protection mechanism. Further, the tokens are 64 bytes long, which matches the length of the Rails masked CSRF tokens approach.

The tokens are attached to the following forms:

  • Token 1: The Sign out Button
  • Token 2: The Update profile button
  • Token 3: The Save jobs profile button

If you reload the page, the values of all of the three tokens change.

Let us consider a subsequent POST that updates my profile.

  • If I submit the intended Token 2, the action is performed.
  • If I submit an invalid token, the action is not performed (the server returns HTTP 422).

So far so good, now to the things I do not understand. The action is also performed, without an error, when I do one of the following requests:

  • Submit another token from the page (say Token 1 or Token 3 from above).
  • Submit Token 2 again. So the old token is not invalidated as being used. This works multiple times.
  • Reload the page, but submit one of the old tokens.

So I guess it comes down to: Why does github implement per-request (even per form) tokens, but never invalidates them / verifies that they belong to the correct form element?

Or maybe I am totally wrong, and github actually implements a different approach (like Encrypted Token Pattern).

2 Answers 2

2

Nonlinear workflow

The question seems to assume a linear workflow - i.e., user loads page A that has token A1; performs an action that requires that token; loads page A again, this time with token A2, and performs an action that requires it.

However, it is perfectly reasonable for users to have multiple pages open in the same session - you can "use a token" in one tab, then switch to another tab that was opened an hour ago, and click a button that submits a token that technically is much older than the one you just used. If the older token would have been invalidated, that would look as an obvious bug from the user's perspective.

Also, it's reasonable to use the back button, which may return to a cached version of the page with the old tokens. You can GET a form with a particular token, submit a POST request with this token, then remember that you need to change one more thing, press the back button (which shows the form again without fetching a new version) and then submit another POST request with (obviously) the same CSRF token that you just used. Again, this is a reasonable workflow, and it should just work, and invalidating the token would break it.

Some invalidation of CSRF tokens would be reasonable, but it should happen in conditions similar to session expiry - either on explicit logout, or a comparably long timeout; not on simply using a newer token, as within a single session it is reasonable for tokens to be used out of order and/or reused.

1

GitHub validates authenticity_token to implement a CSRF protection. You are right that they are using a per form token.

However I am unsure if the token is never getting invalidated. You have ignored the possibility of the authenticity_token being a short living token which is valid for a period of time, say 60 minutes. [This figure is not accurate, I will verify the same and will update the exact figure here.]

Now from the attack perspective let's see what is the additional risk which a reusable token possess over a single use anti CSRF token.

The moment a token is being validated at the server side to verify if the request is forged, the application is protected against CSRF. If the token used is not a single use token, the possible attack vector requires the possession of at least one valid token.

What are the probable ways of getting the one valid anti CSRF token here?

The attacker will need access to the browser memory to steal the token which makes it extremely difficult and an almost impossible attack vector. And also note that, in GitHub, the authenticity_token is valid only during an active session. The moment you log out and login again, any authenticity_token from the previous session won't be valid anymore.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .