Why does Express/Connect generate new CSRF token o

2019-03-28 10:34发布

问题:

As far as I understand there are two approaches in protecting from CSRF attacks: 1) token per session, and 2) token per request

1) In the first case CSRF token is being generated only once when the user's session is initialized. So there is only one valid token for the user at once.

2) In the second case new CSRF token is being generated on each request and after that an old one becomes invalid. It makes harder to exploit the vunerability because even if attacker steals a token (via XSS) it expires when the user goes to the next page. But on the other hand this approach makes webapp less usable. Here is a good quotation from security.stackexchange.com:

For example if they hit the 'back' button and submit the form with new values, the submission will fail, and likely greet them with some hostile error message. If they try to open a resource in a second tab, they'll find the session randomly breaks in one or both tabs

When analizing Node.js Express framework (which is based on Connect) I noticed that a new CSRF token is generated on each request, but an old one doesn't become invalid.

My question is: what is the reason to provide new CSRF token on each request and not to make invalid an old one? Why not just generate a single token per session?

Thank you and sorry for my English!

回答1:

CSRF tokens are nonces. They are supposed to be used only once (or safely after a long time). They are used to identify and authorize requests. Let us consider the two approaches to prevent CSRF:

  1. Single token fixed per session: The drawback with this is that the client can pass its token to others. This may not be due to sniffing or man-in-the-middle or some security lapse. This is betrayal on user's part. Multiple clients can use the same token. Sadly nothing can be done about it.

  2. Dynamic token: token is updated every time any interaction happens between server and client or whenever timeout occurs. It prevents use of older tokens and simultaneous use from multiple clients.

The drawback of the dynamic token is that it restricts going back and continuing from there. In some cases it could be desirable, like if implementing shopping cart, reload is must to check if in stock. CSRF will prevent resending the sent form or repeat buy/sell.

A fine-grained control would be better. For the scenario you mention you can do without CSRF validation. Then don't use CSRF for that particular page. In other words handle the CSRF (or its exceptions) per route.

Update

I can only think of two reasons why single dynamic token is better than multiple:

  1. Multiple tokens are indeed better but have at least one dynamic token like one above. This means designing a detailed workflow which may become complex. For example see here :

    1. https://developers.google.com/accounts/docs/OAuth2
    2. https://dev.twitter.com/docs/auth/implementing-sign-twitter
    3. https://developers.facebook.com/docs/facebook-login/access-tokens/

    These are tokens to access their API (form submission etc.) not just login. Each one implements them differently. Not worth doing unless have good use case. Your webpages will use it heavily. Not to mention form submission is not simple now.

  2. Dynamic single token is the easiest, and the readily available in library. So can use it on the go.

Advantages of multiple tokens:

  1. Can implement transactions. You can have ordering between requests.
  2. Can fallback from timeout and authentication errors (you must handle them now).
  3. Secure! More robust than single tokens. Can detect token misuse, blacklist user.

By the way if you want to use multiple tokens you have OAuth2 libraries now.