How To Stop a CSRF Attack In PHP (And Setting Secure SameSite Cookies)

A few days ago I noticed a loophole in some code that meant it was vulnerable to an attack known as a CSRF attack. Thankfully, whilst it’s a pretty simple attack, it can be a little tricky to execute.

The reason for that is because the attack requires the attacker to actually get the victim to visit a page outside of the application, on their own server.

You see, the attack basically involves taking advantage of the users logged-in session & submitting a form to make a change on their account without them realizing.

So the attacker’s page will contain a form from your website that automatically submits on the page being opened. As an example, they could have copied the “password change form” from your site.

The attacker will then set their own password in the form & trick the victim into visiting the page. Then, when the victim visits the page, the form will automatically submit & it will change their password to that set by the attacker (by taking advantage of their current logged-in session).

To prevent this, I did two things:

  • Added a hidden code to forms (encrypted and time-sensitive)
  • Marked session cookies as “Secure” and set the SameSite value to Lax (to prevent them from being shown in “GET” requests)

I’ll run through them in more detail below:

Adding a hidden code to prevent CSRF

When a user logs into the application, I create a unique, encrypted value & store it in their session like so:


// Set a session token for security (CSRF prevention)
if (empty($_SESSION['CSRF']){
$csrf_rand_string = generateUniqueString();
$_SESSION['CSRF'] = $csrf_rand_string;
}

Then, on my forms I add the value as a hidden input field:


<input type="hidden" name="csrf_token" id="csrf_token" value="<? $_SESSION['CSRF']; ?>">

And finally, in the file that handles the form from the post request, I check that the provided value matches the one in the users session.


// Check value matches the one in session

if ($_SESSION['CSRF'] != $_POST['csrf_token']) {

die("CSRF attack");

}

If it doesn’t match, it looks like a CSRF attack so the script doesn’t execute to protect the user.

Setting secure Lax SameSite cookies

If the cookie’s SameSite is set to “none” & isn’t secure, then from my understanding, the attacker could potentially get the data from the session cookie in the “Get” request.

Obviously, that could cause all sorts of problems.

So not only did I set the cookie to secure, but I also set it to Lax. By setting it to Lax, the cookie will NOT show up in any “GET” requests (so be aware that if your “GET” requests need the cookie, it could cause problems – so use “POST” instead).

Here’s how I set the cookie:


setcookie("secure_cookie", $cookie_value, [
'expires' => time() + (86400),
'path' => '/',
'domain' => 'yourdomain.com',
'secure' => true,
'httponly' => true,
'samesite' => 'Lax',
]);

And that, hopefully, should protect you from CSRF attacks.

Leave a Comment