Search code examples
phphtmlsessiontokencsrf-protection

Issues while implementing a PHP session token


I'm trying to implement CSRF protection to a webform. Its a one page website that captures data, posts it to a PHP file which then emails me the information. The webpage functions as intended (minus the security).

I'm having the following issues:

  1. I can't work out how to implement the PHP code to set the token; converting index.html to index.php loads a blank body. I think that resolving this would likely fix my issues.
  2. When I try and call token.php from jQuery, I get a 403 error.
  3. When I try and run the script in a 1-pixel iframe, I get a 500 error (I assume as it's being run on HTML).

token.php

<?php
session_start();

if (empty($_SESSION['token'])) {
    $_SESSION['token'] = bin2hex(random_bytes(32));
}

$token = $_SESSION['token'];
?>

formsubmit.php

<?php 
if ($_SERVER["REQUEST_METHOD"] == "POST") {
    if (!empty($_POST['token'])) {
        if (hash_equals($_SESSION['token'], $_POST['token'])) {
            $emailbody = 'Name: ' . $_POST['m_title'] . ' ' . $_POST['m_firstname'] . ' ' . $_POST['m_surname'] . "\n"
                . 'Email: ' . $_POST['m_email'] . "\n"
                . 'Phone: ' . $_POST['m_phone'] . "\n"
                . 'D.O.B: ' . $_POST['m_dob_day'] . ' ' . $_POST['m_dob_month'] . ' ' . $_POST['m_dob_year'] . "\n"         
                . 'Postcode: ' . $_POST['m_postcode'] . "\n"
                . 'Lenders: ' . $_POST['m_bank1']  . ',' . $_POST['m_bank2'] . ',' . $_POST['m_bank3'] . ',' . $_POST['m_bank4'] . ',' . $_POST['m_bank5'] . ',' . $_POST['m_bank6'] . ',' . $_POST['m_bank7'] . ',' . $_POST['m_bank8'];               
            mail('**removed**', 'Web Lead', $emailbody);
            header('Location: **removed**/thankyou');
            exit();
        }   
        else {
            echo "token invalid";
        }
    }
    else {
        echo "token blank";
    }
}
else {
    echo "invalid request";
}
?>

My jQuery attempt in index.html

<script>
$(document).ready(function() {
    $.get('token.php');
}); 
</script>

Provided the PHP above is not riddled with errors, I had assumed that successfully converting it to index.php would resolve my issues, but I am having difficulty doing so.


Solution

  • There's a few problems with your code:

    token.php needs to output the token. Which is as simple as echo $token;

    The main problem is that you've used the following jquery:

    $(document).ready(function() {
    $.get('token.php');
    });
    

    What this means is that when the page (index.html) is loaded, your browser makes an ajax request to token.php. However... nothing further happens. You're not actually doing anything with the response (output) from token.php.

    What you need to do is place the response from token.php into your web page. Going off the code you've posted I assume there is a form field with the name "token"? If this is the case then you would make the ajax request like this, which writes the response of token.php to that field:

    $(document).ready(function() {
        $.get('token.php').done(function(data) {
            $('input[name="token"]').val(data);
        }).fail() {
           // Handle ajax request failure here
        });
    )};
    

    To answer your questions:

    1. $_SESSION['token'] = bin2hex(random_bytes(32)); means that the variable $_SESSION['token'] contains your token. That's where you are setting it.

    2. If you're getting a 403 error requesting token.php in the ajax request this means that the server won't process that request. So it could be a permissions issue or some other problem. Try putting die('test'); at on the first line of the script and then executing /token.php in your browser. Can you access it, or does it give you an error? If it's accessible it will say "test". Check the servers error log if you're unsure why you can't access it. Based on the code, it's unlikely that the URL to the script is wrong, or that'd give you a 404 error.

    3. It's unclear why you're bothering with: "When I try and run the script in a 1-pixel iframe". I assume this is an attempt to write the response of token.php back to the page. That can be solved by my answer above.