Link Search Menu Expand Document

Cross-Site Request Forgery in PHP

Prevention

PHP does not provide a built-in protection against CSRF attacks; developers must manually implement it by checking the session tokens, or by using one of the many, well-tested libraries, and frameworks.

PHP

The following steps show how to protect the action.php?do=logout endpoint from CSRF attacks using the Synchronizer Token Pattern.

  1. Upon successful authentication, a random token is generated in a secure way and stored as a user’s session variable.
<?php
$_SESSION["csrf_token"] = bin2hex(random_bytes(32));
?>
  1. Any request to the state-changing endpoint action.php?do=logout must now pass the token as an HTTP parameter. The following snippet shows how to write an <a href> tag to pass the token as a GET parameter.
<a href="action.php?do=logout&csrf=<?php echo $_SESSION["csrf_token"]; ?>">
  1. The following snippet shows how to protect a form by passing the token as hidden field.
<form>
   <input type="hidden" name="csrf" value="<?php echo $_SESSION["csrf_token"]; ?>">
</form>
  1. The file action.php must now validate the csrf parameter that matches the token saved in the users’s session before executing the protected code. Use a secure hash comparison function such as PHP hash_equals().
if (!empty($_REQUEST["csrf"]) && hash_equals($_REQUEST["csrf_token"], $_SESSION["csrf_token"])) {
   // The token is correct, execute the functionality.
}
else {
   die("Anti CSRF token is missing or wrong.");
}

OWASP CSRF Protector

OWASP CSRF Protector is a standalone php library for CSRF mitigation in web applications. Follow the instructions on the project page to install it. To use it, simply include the library and call the init() function.

<?php
include_once __DIR__ .'/vendor/owasp/csrf-protector-php/libs/csrf/csrfprotector.php';

// Initialise CSRFGuard library
csrfProtector::init();

Symfony Framework

The Symfony form component provides automatic CSRF protection by default. Other types of resources, like regular HTML forms or any other state-changing routes, must be explicitly protected by generating and checking CSRF tokens manually.

Consider using a regular HTML form to delete a resource. First, use the csrf_token() Twig function to generate a CSRF token in the template and store it as a hidden form field.

<form action="/delete" method="post">
    <input type="hidden" name="token" value="{{ csrf_token('action-delete') }}"/>
</form>

Then, get the value of the CSRF token in the controller action and use the isCsrfTokenValid() to check its validity.

public function delete(Request $request)
{
    $submittedToken = $request->request->get('token');

    if (!$this->isCsrfTokenValid('action-delete', $submittedToken)) {
      // The token is not correct, redirect to the index.
      return $this->redirectToRoute('index');     
    } 

    // The token is correct, execute the functionality.
}

References

OWASP - Cross-Site Request Forgery Cheat Sheet MITRE - CWE 352 OWASP - CSRF Protector Symfony - How to Implement CSRF Protection