Using Random Form Token to Help Protect Against CSRF

Posted in Tutorials

Tweet This Share on Facebook Bookmark on Delicious Digg this Submit to Reddit

Suppose we have the following form in index.php …

the form

the form

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…

create form token

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 …

checking request method

checking request method

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…

check form token

check form token

If form token is good, we also check if the token was generated recently (within five minutes) …

check for recent token

check for recent token

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.