0

I have had this functionality (csrf protection) for quite some time now. The thing I never liked about it is that it saved csrf tokens in the session file. While it may not be a real problem I see it as a pain because eventually tokens may build up so I need to utilize a gc mechanism and keep track of what was issued when and it all just seems too much for me.

So I spent the last couple of days working on improving this "imperfection" and here's what I've come up with. I will not be posting any real code I'll just explain the idea.

When a form is issued, by default a token is generated. The token consists of some data unique to the user, like session id or user id, combined with the current datetime string in format YmdHi. All of this is encrypted using password_hash and I have thought of further obfuscating the hash by swapping parts of it but haven't done that yet.

The validation was kind of tricky and turned out to be quite a disappointment. In order to validate a token I create a loop that hashes the same information about the user with the date and subtracts 1 minute on each iteration. Number of iterations is equal to the amount of minutes the token is valid for, which is not stored in the token therefore all tokens must be valid for the same amount of time, which doesn't seem like a disadvantage to me, yet.

I want to ask you if you see any possible flaws and exploits with this concept.

The only possible problem I see is that tokens are not actually kept by the server so theoretically anyone can assemble a valid token, however I think it's highly unlikely anybody would spend days in collecting tokens and would happen to have a super-computer in their basement to run statistical analysts on, which is again no guarantee that they would be able to crack the encryption.

Anyway I guess I should stop exposing my incompetence and let the smart people decide what's up.

php_nub_qq
  • 787
  • 1
  • 6
  • 13
  • I'm not sure I understand the GC problem that you are trying to solve. – Neil Smithline May 29 '15 at 21:40
  • @NeilSmithline If I start browsing around and visit a bunch of pages with forms on them I can easily collect 20+ tokens in my session file. All of this information will need to be parsed on each subsequent request for the next x minutes before the tokens are collected as garbage. You may say it is negligible but with large amount of users it may not be. Another reason that is not scientifically valid - it is just irritating me. – php_nub_qq May 29 '15 at 21:48
  • Can't you use one token per session? That is a [common solution](https://www.owasp.org/index.php/CSRF_Prevention_Cheat_Sheet#General_Recommendation:_Synchronizer_Token_Pattern). – Neil Smithline May 29 '15 at 21:57
  • @NeilSmithline but what happens if *somehow* an attacker snatches the token? In my previous scenario it would be valid for 5 minutes at most, but a session length may be hours. – php_nub_qq May 29 '15 at 22:10
  • If an attacker steals the token and is able to engineer using it against the corresponding user in a CSRF attack, then the attack will succeed. But things are awfully broken already. Coincidentally, [this](https://stackoverflow.com/questions/30539259/how-long-should-the-lifetime-of-a-csrf-token-be) similar question was just answered. It suggests using `HMAC(Session ID, secret)` for the CSRF token. That way you don't need to store anything in your DB. Do read [this OWASP reference](https://www.owasp.org/index.php/CSRF_Prevention_Cheat_Sheet#General_Recommendation:_Synchronizer_Token_Pattern). – Neil Smithline May 29 '15 at 22:16

4 Answers4

2

Personally, I've never seen someone roll their own alternative to an industry standard for security and have it turn out as an acceptable solution. One phrase I hear thrown around a lot in the security community is "Don't roll your own".

Reading over what you are doing it seems like you might be too smart for your own good. This seems like a complex solution that could be less secure than just creating a CSRF token. If someone inherits your code their head will likely explode.

I'm not sure sure where you are storing the token you are creating, but if it's in a cookie there is no protection at all. If you are placing your token into a hidden field in the form then you are essentially using a weirdly modified version of double submit cookie CSRF protection.

My advice - don't go down this road. At best it will be more complex and as secure. At worst it will be more complex and won't work.

Abe Miessler
  • 8,155
  • 10
  • 44
  • 72
1

I also think you may be over thinking/engineering here. It seems to me that your combining session authorisation with CSRF protection. I'm not a PHP programmer, but in all the languages and frameworks I've used, this level of protection is laready available in some form. Your probably better off using an established solution rather than rolling your own.

Most of the solutions I've seen don't generate new tokens for every request, Usually, there is a single token per session and it is stored with session data, so there is no GC issue tow orry about. All the token should really be doing is verifying that the response has come from the same client the form was sent to. It is usually assumed that the session is already authorised. If your concerned about the session authorisation, then you have bigger problems anyway because the attacker could just hijack the session, in which case, they will get a 'legitimate' token when they request the form.

Tim X
  • 3,242
  • 13
  • 13
1

If you want to avoid extra storage in your application's database or session, why not simply use the Double Submit Cookies prevention method.

Here you simply send a cookie value duplicated via another mechanism along with each request, such as inside a hidden form field.

For example, generate a cryptographically secure random value with 32 bits of entropy. Store this value in a cookie named csrftoken and in each form as a hidden field named csrftoken. When each form is submitted you check that the csrftoken sent from both delivery mechanisms match.

Please do not use Session ID for this - the OWASP page recommends using a separate value from Session ID because it affords extra protection against Session ID leakage. Protect your Session ID as much as possible and not exposing it unnecessarily will help maintain secure session management. Remember that you do not have to store this extra token server side. All you are doing functionality wise is checking the POSTed token matches the cookie - it doesn't really matter what value it is to your server. All that matters is that the same value is submitted via cookie as via the form, as an attacker would not know this value and therefore would not be able to submit it themselves.

Note you do not need a separate CSRF token per form. The only way an attacker is going to retrieve a token is through another vulnerability (such as XSS). And if they can perform XSS, they have already defeated this type of CSRF protection. You should however regenerate the token for each new session to prevent any type of persistent brute force against it.

SilverlightFox
  • 33,408
  • 6
  • 67
  • 178
  • If I understand correctly, this will disable me to have separate tokens for separate requests. This is the same as creating only one token per session but instead of storing the token in the session I would be storing it in the bandwidth. If I decide to go with the 1-token-per-session method I guess it wouldn't be a problem to store that in the session file. I think I need to determine whether the extra step I'm taking with expiring tokens in a really short term is worth the trouble. – php_nub_qq May 30 '15 at 12:47
  • See [this question](http://security.stackexchange.com/questions/22903/why-refresh-csrf-token-per-form-request) for some insight on why you don't need to expire tokens earlier than per session. – SilverlightFox May 30 '15 at 12:49
0

You have seen that there is a problem when you use time as part of the value when you create a hash - time is constantly changing. But there is some utility in using it in a csrf token. The solution to allow for small variations is to not hash the values to get the token but instead use symmetric encryption of a representation of the time along with some other values.

However using sessions is usually more sensible. And if you have 'users', 'passwords' then by definition you have a session and therefore must be doing state management. There is really no reason not to use the session to maintain the csrf tokens.

symcbean
  • 18,278
  • 39
  • 73