Using Random Form Token to Help Protect Against CSRF
Suppose we have the following form in index.php …
that will POST to do.php to do something. To help protect against CSRF (cross-site request forgery), in do.php we have to make sure that it is a POST request (and not a GET request) and then make sure that the POST request actually came from our form and not our attacker’s form.
In our form, we create a randomly generated token and save to session and send as part of the data sent in the form. In the receiving end at do.php, we verify that the token stored in session is the same one that came via the form data. This is one way to help reduce the risk of CSRF (see link here for more info about it).
Sending the Form Token
We are working with sessions, so at the top of index.php, we call session_start. Then we define our PHP function createFormToken() that generates some random string of characters, which will be our form token. Using md5(uniqid(rand(),TRUE)) is just one possible way of generating it…
However, depending on the nature of your application this form of generating the token is not secure enough, because the rand() is not random enough and the use of the token is not one-time-use. While an alternative way is to do md5(time() . $_SERVER[‘REMOTE_ADDR’]), a better way is to do as described in this article.
But for the purposes of keeping the example simple, we continue with it…
After generating the token we store it in $_SESSION[‘formtoken’] and return it out from the function. So that in our HTML form, we call the function createFormToken() to echo out the token as a value in a hidden form field.
Note that we also store the timestamp of the token into $_SESSION[‘formtoken_time’] as an additional safety measure to check to make sure the token receive is recent.
Receiving the Form Token
In do.php, we do session_start() and make sure we are receiving a POST and not a GET …
We terminate with “Bad request” if not a POST. When all is good we echo out a greeting of Hello. Note that we use htmlspecialchars() on the post data to sanitize user input.
Now we add the check for our form token…
If form token is good, we also check if the token was generated recently (within five minutes) …
If the current time (at processing the form) is more than 5 minutes from the time the form was displayed (which was when the token was generated and save to session), then user is taking too long. Exit out with error message.