playframework/playframework — pull request #2367 by jroper (2014-08-28 03:43:06+00:00)
Title: CSRF token disaster when upgrading Play 2.1.5 -> 2.2.2-RC2

TL;DR after upgrading to Play 2.2, any user who has visited your website before the upgrade will be unable to POST.

Demo:

1. wipe your browser's cookies for localhost
2. `git clone https://github.com/adamhooper/play-csrf-token-failure.git`
3. `cd play-csrf-token-failure`
4. `git checkout play-2.1.5`
5. `sbt run`
6. Browse to http://localhost:9000. Notice that there is a CSRF token. Click the button; you should see "your CSRF token is valid"
7. Kill sbt; `git checkout play-2.2.2-RC2 && rm -r target project/target`
8. `sbt run`
9. Browse to http://localhost:9000. Notice that there is no CSRF token. Click the button; you will see "Invalid token found in form body"

What happens:

1. On Play 2.1.5, a token is stored in the session.
2. On Play 2.2.2-RC2, the token is read. CSRFAction's `getTokenFromHeader()` detects that a token is present, but it does _not_ detect that the token is not _valid_. In other words, `getTokenFromHeader()` returns `Some`, but `CSRF.getToken()` returns `None`.
3. There is no code path that will actually generate a new CSRF token for the user. That means the user cannot POST until he/she deletes the session cookie.

Workarounds:

* Change `csrf.sign.tokens` to `false`, introducing the BREACH vulnerability signed tokens solve
* Change `application.secret`, effectively destroying all sessions
* Change `csrf.token.name`, which works if you haven't hard-coded "csrfToken" anywhere :)

We chose to change `application.secret`. It's wince-invoking, but what else could we do?

I haven't tested, but I imagine this same bug prevents anybody from editing `csrf.sign.tokens` after anybody has visited the website.