Protecting from Cross Site Request Forgery (CSRF) in PHP

What is CSRF?

Say you are logged in to your bank account website. Because you are authenticated, you might be able to do something like transfer funds to another account through the website. This is totally fine, because the cookie on your computer verifies that you are authenticated to the website, and the website checks to make sure that your computer is the one holding the session for your current log in. Now say someone knows what kind of post data needs to be sent to perform that same task (which can easily be gleamed by looking at the HTML of the bank’s website). If they could get your computer to somehow submit the right post data to the right place, they could make bank transfers from your computer, using your session. This is how a CSRF attack works.

Often, CSRF attacks will be masked in an image tag because browsers will attempt to load the resource in the src tag. So if I were to send for instance:

<img alt="" src="" />

in an email, or place it quietly on the bottom of a webpage, comment on a website, or whatever, your browser would attempt to load the resource. This means that a CSRF attack is able to execute actions in the unsuspecting user’s browser as though it was performed by them. Additionally, as far as the bank knows, it was a normal action taken on the user’s behalf.

Protecting Against CSRF with PHP

Protecting against CSRF is pretty easy. We just need to generate a token in our web forms that only the user and our server knows. Unless a hacker is somehow able to gleam the token (which requires a far greater level of intrusion than emailing an img tag), they should not be able to falsify the token.

When a user logs in, you should generate a CSRF token that you can use throughout your website:

  //On login
  $_SESSION['csrf'] = md5(mt_rand() * microtime());

We don’t want to use a totally predictable token like microtime because a hacker might be able to compute what the token should be so we multiply microtime by a pseudo-random number and then hash the result. This token will be very unique, random, and long, which are the three requirements for a good csrf token.

Now that we have a csrf token we want to put it in any web forms that a user might submit while they are logged in.

<form action="/forms/transferFunds.php">
  <input type="hidden" name="csrf" value="<?= $_SESSION['csrf'] ?>">
  Amount: <input type="text" name="amount">
  Account to Transfer to: <input type="text" name="send_account_num">
  <input type="submit" value="Send Funds">

We have a hidden input (not for security) called csrf with a value equal to the unique hash that we computed for the currently logged in user. Now when they submit the form, we simply check to make sure that the CSRF token is what we expect it to be:

  if($_SESSION['csrf'] != $_POST['csrf']) {
    die("You may be the victim of an attempted CSRF attack!");
  else {
    //This user is legit, transfer the funds over

That’s it! Now your form handlers will only work if they were submitted by the forms generated by your server for that specific user.

Leave a Reply

Your email address will not be published. Required fields are marked *