34

I am fully aware of CSRF and have already implemented some safe forms, but I have never been happy with the results yet.

I've created tokens as a md5 of username, form info and a salt and stored it in a session. This has a problem with tabbed browsing (can be fixed by keeping an array of active hashes) and with timing. I would like my hashes to work only for eg. 10 minutes, but how do I put a time-related variable in the hash?

Can anybody point me to a good resource describing how to make CSRF security properly?

Anders
  • 64,406
  • 24
  • 178
  • 215
naugtur
  • 1,095
  • 2
  • 12
  • 15

5 Answers5

27

There is a good explanation on OWASP: OWASP CSRF Prevention Cheat Sheet

In short they say that there are two ways:

  1. 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.

  2. 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
Andreas Arnold
  • 2,353
  • 19
  • 19
  • The OWASP page linked above has been updated to recommend "Verifying Same Origin with Standard Headers" checks in addition to the tokens described. – Russbear Dec 12 '17 at 18:55
25

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.

AviD
  • 72,138
  • 22
  • 136
  • 218
  • 12
    OK, that doesn't quite answer the question. It's a site about security, not webapp development. I know I should use existing solutions for any security, but here I expected to get to know how such a solution is created. – naugtur Nov 12 '10 at 09:03
  • 1
    Moreover - I want to use CSRF protection in an app working on a tiny embedded device. Know any frameworks for that in bash or perl without modules? ;) – naugtur Nov 12 '10 at 09:12
  • Heh, fair point about the embedded - you're serving up a web app from that? Wrt the first comment, I would say proper security includes "Don't re-invent the wheel", but if you need it for embedded - or just interested in the theory - I'll update my answer. – AviD Nov 12 '10 at 10:27
  • Thanks! I'm glad I convinced you to answer me that easily. People often get really mean when I ask about things that should not be re-implemented. ;) – naugtur Nov 13 '10 at 21:43
  • 2
    Well, you did make two convincing arguments: 1. I want to understand, and not just be a code monkey :), 2. I have an edge case (embedded usually fits the bill), and there is no wheel there yet. – AviD Nov 14 '10 at 00:38
3

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.

Nasir
  • 141
  • 2
  • 2
    Timestamps only prevent replay outside of a configured timeout. You should use a nonce for that. Also, another good property of the Encrypted Token pattern is that it doesn't require server state (i.e. no session) and thus makes horizontal scalability easier (e.g. no session replication, no sticky session). – skuro Apr 29 '15 at 09:07
-2

I don't think that there's much wrong (though I may be wrong ;) with including a timestamp in the token to be hashed and including this timestamp as a hidden form field - maybe hex-encoded as basic obfuscation - you can then check for staleness on submission.

Nev Stokes
  • 458
  • 3
  • 10
  • 4
    A timestamp doesn't have sufficient entropy. If you used it as input to a PRF, together with a cryptographic key that is never revealed to any client, that might work, but it is needless complexity. Better to avoid building your own, and rely upon some well-vetted implementation, as AviD recommends. – D.W. Jan 10 '13 at 02:34
-2

I tend to think that token based CSRF protection can be fairly easily broken: an attacker just need to know how to request a CSRF protected page, normally these pages have the token as a hidden field. Attacker then grabs the token from the page (fairly trivial) and use it to construct a CSRF attack.

pauld
  • 1
  • 7
    Well, if you're using a program that can query the page, you're as much of a user to me as anybody else. anti-CSRF is to prevent sending request from another site through an ordinary browser (because the user is logged in and the browser sends session information). If your attacking code can get the page contents, you're not doing a CSRF anymore. You can just post anything. – naugtur Feb 19 '13 at 19:56