1

I am trying to add a token to my forms in order to prevent CSRF attacks

But the token validation isn't working

Here is the input field which holds the token

<input type="hidden" name="auth_token" value="<?php echo $_SESSION['auth_token']; ?>">

And here is the token validation code

    if ($_SERVER["REQUEST_METHOD"] == "POST") {

     // Validate token to avoid CSRF

        $auth_token = $_POST["auth_token"];

        if (!$auth_token || $auth_token != $_SESSION['auth_token']) {
    // show an error message
    echo '<h1 class="error">Error: invalid form submission</h1><p>Your request was denied as this request could not be verified.</p>';
    // return 405 http status code
    header($_SERVER['SERVER_PROTOCOL'] . ' 405 Method Not Allowed');
    exit();
}

// process form here

}

It doesn't work and it returns the error message in the if block

Browyn Louis
  • 218
  • 3
  • 14
  • 2
    This really needs some basic debugging. e.g. work out *why* the test is failing. echo out the values you are comparing. Figure out if the token has changed or if it isn't set for starters. – Quentin Mar 07 '22 at 08:10
  • 2
    A 405 error is wrong here. POST methods are allowed. It's the data in the POST request that is wrong. [403 would make more sense](https://stackoverflow.com/questions/23478370/what-response-should-be-sent-back-a-when-cross-site-request-forgery-csrf-is-de). – Quentin Mar 07 '22 at 08:11
  • @Quentin I compared the values and they happen to be different, but i can't seem to figure out why – Browyn Louis Mar 07 '22 at 08:31
  • From the information you've provided, nor can we. It probably has something to do with the code you use to generate the tokens (and specifically *when* you call it) but you haven't shared that with us. – Quentin Mar 07 '22 at 09:15

1 Answers1

1

I presume that the submitted auth_token value is something random such as hwm7wherlwkju or whatever. Testing !$auth_token could give special results, depending if it's missing or if it contains "1", "true" or "". Secondly, use !== instead of != to avoid automatic type casting in the comparaison.

So I would replace your "if" condition with this:

session_start();

// 1) Check if the recieved token is valid.
if (!isset($_POST['auth_token']) ||
    !isset($_SESSION['auth_token']) ||
    $_POST['auth_token'] !== $_SESSION['auth_token']) {
    // Show an error message.
    echo "<h1 class=\"error\">Error: invalid form submission</h1>\n" .
         "<p>Your request was denied as this request could not be verified.</p>\n";
    // Return a 403 error.
    http_response_code(403);
    die();
}

// 2) Generate a new token for the next request if you are displaying a page with a <form>.
$_SESSION['auth_token'] = bin2hex(random_bytes(20));

About the token value generated, I think you should also check that you are not generating a new value in the session on each request before doing the comparaison for validation. The comparaison should be done first and then a new token value should be generated and stored in the session.

Patrick Janser
  • 3,318
  • 1
  • 16
  • 18
  • Thanks alot, the last part of your answer was the solution. However won't regenerating the token on every new request pose a problem for users using my site on multiple tabs ? – Browyn Louis Mar 07 '22 at 12:05
  • Well, what you could do is generate a token for each `
    ` with a specific id, meaning that you could store a dictionnary with multiple tokens, each one affected to the generated page. Each time you validate the token you delete it from the list. And ideally, each token should have a max age, let's say 30 minutes or so.
    – Patrick Janser Mar 07 '22 at 12:25
  • Oh.. I guess that works fine.. – Browyn Louis Mar 07 '22 at 12:26