views:

892

answers:

3

I have a web form and I'm using PHP. I'm aware that forms can be manipulated (I believe it's called replay attack or a man-in-the-middle attack). So I'd like to use some authenticity token as a hidden field.

The threat possibilities that I'm aware of are:

  • Attacker hijacks the legitimate user's form (this I believe is the man-in-the-middle attack)
  • the legitimate user is himself the attacker: he gets the form, reads the token but uses it to send dangerous data (this I believe is the replay attack)

Before I get to the questions, please correct me if anything I said so far is incorrect, because maybe my understanding is flawed.

Now to the questions:

  • What is the best practice to generate this token so that the form without it gets rejected (for example, salting?).
  • What do people do to make sure that the token isn't being replayed.

Thanks in advance.

Edit: I'm reading more on the topic with the help of your answers. Anyone who helps out with a useful answer/comment gets a +1 :)

New small questions based on comments:

  • Is session hijacking the same as man-in-the-middle attack?
A: 

The method used to generate the token is not highly important. The important thing is that the token gets to be used only once. Keep a list of tokens generated for the user in the user's session. If the user submits a form and the token submitted isn't in the session, you can reject the form.

Protecting against man-in-the-middle is a little difficult. A common technique I've seen is including all of the hidden form fields in the hash function to generate the token, and then regenerating the token based on known hidden fields. However, that would only protect against hidden field manipulation, which may not be the ultimate target of the man in the middle.

When the user successfully submits a form with the token, delete the token from the session, so any replay of that submission will fail. However, all it takes is the user requesting the form again to generate another token. That new token can then be used in subsequent automated attacks. In other words, form tokens are useful against CSRF, but not highly effective against automated replay and man-in-the-middle attacks.

Likewise, you're going to want to adapt your application to not require the use of the user's back button on forms. If there's an issue with their submission, you're going to need to return the form back to the user with their data filled in. If the user hits her back button to correct the error, her submission will then fail due to the now invalid token.

Also, to be frank, by the time you need to worry about request replays and man-in-the-middle attacks, your user's connection has already been compromised, and there's probably not much you can do to mitigate any damage. SSL alone is an adequate protection level against MITM and replay, and if you're that worried about it, you'll be running under SSL...

Charles
Or, in a few words: Just use SSL.
Steven Sudit
No, SSL doesn't do anything to prevent XSRF attacks, so "Just use SSL" doesn't cover it.
erickson
It does protect against session hijacking, which is the other concern Chris had. XSRF just isn't that big a risk unless you violate the rules psychotik outlined by allowing gets to change things.
Steven Sudit
If the attacker controls the user's browser, then there is no way to protect the user. All protection implemented in the browser can be circumvented by the attacker.
Exception
+1  A: 

You mentioned CSRF in the title but didn't really cover it in your question.

You can read about it in detail online, but CSRF is basically an attack that lets a legitimate user submit to a site unknowingly. For instance, if SO wasn't protecting against such attacks, I could craft a form that causes your SO profile information to be changed when you click on this bad form of mine, expecting something else to happen ("Win a Million bucks!! Click here!!"). This form would use your browser cookies to auth you with SO and make it appear to SO that you were legitimately submitting updates to your profile.

To protect against this, you really want to do a couple of things:

  • ensure that GETs don't cause updates (for instance, don't post a new status update to a user's profile using query params on a GET)
  • ensure that all POSTs are accompanied by a hidden field that lets you validate that the form was generated by your service and not by someone else, and that it was for the intended user. So this hidden field must be generated by the server each time it sends down the html for the form, and should be unique for that user for that session.

An alternative to that second item is to check that the referrer is always your site or a site you expect the POST from. I don't encourage this for non-HTTPS sites because some browsers/networking hardware strip out referrers, and it's not always reliable that a referrer exists.

psychotik
I mentioned CSRF only in the title, because I felt it was somehow related to the form authenticity token, just not sure how.
Chris
It definitely is - hopefully my response was clear enough about how/why.
psychotik
Yep, it's getting clearer now, thanks for the answer. I voted it up
Chris
+1  A: 

To generate that token, here's a strategy I use that seems to work well.

  • Set a cookie to a random value (generated on the server using the usual tricks) and set it to expire according to your needs.
  • When serving up a page with a form, embed a hidden field whose value equals the value of this cookie.
  • When handling the post, validate that this field exists, and that the value matches the user's cookie
psychotik