What is the correct way to implement anti-CSRF form tokens?
There is a good explanation on OWASP: OWASP CSRF Prevention Cheat Sheet
In short they say that there are two ways:
Add a random token to each user session. Only if this token is present and correct will the changes be applied, otherwise the request should be rejected. It is important that the token is only sent with a POST request, since GET requests can leak the token to different places (browser history, log files, etc.).
It is also possible to generate a token per request, but this leads to usability problems. For example the back button wouldn't work properly anymore. But of course the security would be increased.
Also make sure (to enhance security even more) that the token is only sent over TLS, so there won't be any man in the middle problems.
The other option is to use some sort of challenge - response (for example CAPTCHAs or one-time tokens).
The OWASP-Page also lists a few measures that don't work. These are
- Using (secret) cookies
- Not using GET-requests
- Multi-Step transactions
- URL Rewriting
The best way to build CSRF protection properly:
Don't.
Most common frameworks have this protection already built in (ASP.NET, Struts, Ruby I think), or there are existing libraries that have already been vetted. (e.g. OWASP's CSRFGuard).
Another option, depending on your context, is to enforce reauthentication of the user, but only for specific, sensitive operations.
As per your comments, if you need to implement this yourself, your solution is not too bad, but I would simplify it.
You can generate a crypto-random nonce (long enough), store that in session memory, and change it every X minutes (you store the expiry time in session too). In each form, you include the nonce in a hidden form field, and compare that value when the form is posted.
If the form token matches, you're clear. If not, you can accept e.g. the previous token as well, to handle the edge cases.
Though I must say that I've seen too many failed attempts at implementing this by oneself, to really recommend this path. You're still better off finding a minimal package to do this for you.
In addition to @Andreas Arnold answer there is an alternative. I implemented the Encrypted Token Pattern
from OWasp.
Apart from preventing csrf attacks, it has the added advantage of implementing object security.
Only those who are authorized to modify objects, can do so. Others will not have the correct/valid cipher text or key. I usually encrypt sessionId
, timestamp
, userId
, and/or recordId
.
timestamp
prevents replayAttacks.
sessionid
isolates changes to this session only
userId
isolates changes to this user only.
If you include recordId
, then at the cost of extra processing you gain more security.