PHP Classes

Modern PHP CSRF Protection Example using a PHP Forms Library - PHP Forms Class with HTML Generator and JavaScript Validation package blog

Recommend this page to a friend!
  All package blogs All package blogs   PHP Forms Class with HTML Generator and JavaScript Validation PHP Forms Class with HTML Generator and JavaScript Validation   Blog PHP Forms Class with HTML Generator and JavaScript Validation package blog   RSS 1.0 feed RSS 2.0 feed   Blog Modern PHP CSRF Prote...  
  Post a comment Post a comment   See comments See comments (2)   Trackbacks (0)  

Author:

Viewers: 165

Last month viewers: 22

Package: PHP Forms Class with HTML Generator and JavaScript Validation

Categories: Security, Validation

Cross-Site Request Forgery (CSRF) attacks are means that badly intentioned users may use to make users of a site perform actions without intention. For instance, a successful CSRF attack can make e-commerce purchases, and the purchased goods are delivered to an attacker's location address.

Good Web sites are protected against CSRF security attacks. Attempts to perform these attacks are ignored.

As a responsible Web developer, it is in your best interest to learn how to prevent these attacks so your PHP Web projects can be more secure.

Please read this short article to learn more about CSRF attacks and a practical example of protection against CSRF attacks in your Web form pages generated and processed by PHP.




Loaded Article

In this article, you will learn:

What are CSRF attacks?

How to Prevent CSRF Attacks Using Protection Tokens and SameSite Cookies

Example of How to Prevent CSRF Attacks in Practice with Code that Uses a PHP Forms Generation Library Plugin to Generate Protection Tokens

How the HTML of the Form Will Look Using the CSRF Protection Token

How to Download or Install the PHP Forms Generation Package Using PHP Composer


What are CSRF attacks?

You know that anybody can put a link to your site on pages of another site, so when the user clicks on that link, the browser is sent to your site page. Likewise, you can put a form on another site making the form submission URL point to a page on your site, so when the user submits the form, the browser is sent to your site page.

Besides that, it is also possible to put forms on a Web page with hidden fields and use JavaScript to emulate form submission. This way, a form on a page of one site can be submitted without the user visiting that page being aware that the form is being submitted without his knowledge or consent.

If you are not familiar with CSRF attacks, you probably may not be aware that it is possible to emulate clicks in links or form submissions without having the user click on anything. All that is needed is to execute some JavaScript code emulates those actions.

As you may have understood by now, you can write some JavaScript code that can emulate those actions just by entering a malicious page.

Let's say that you have an e-commerce site. Suppose you allow customers to set the delivery address of purchased goods to a specific address. In that case, an attacker may attract users to pages on another site that make the users submit hidden forms to your e-commerce site.

This can be used to perform a CSRF attack to mislead users of your site to set the delivery address of products bought on your site to the physical location of the attacker, so he can get the products purchased on the site by your actual customers.

Then the attacker may also use hidden forms in other site pages to make your real users purchase products without knowing. This way, the attacker can get products from your company, which are paid for by the victims, who are your actual users.

How to Prevent CSRF Attacks Using Protection Tokens and SameSite Cookies

There are several techniques to protect a site against CSRF attacks.

One technique uses a token string that is passed in hidden form input. That taken is a string generated randomly on the server side when the form is generated.

The token is valid for a short period and can be stored on PHP sessions or encrypted using a secret key. This makes it hard for an attacker to know in advance what the token is so that he can forge the submission of a hidden form on another site.

Another technique is to use SameSite cookies. These are regular cookies sent by browsers only if the page accessed is the same as the site that sent the cookie to the browser.

I have mentioned this technique in more detail in another article about PHP built-in support for SameSite cookies in more recent PHP versions.

Ideally, you should combine SameSite cookies and protection tokens to implement stronger protection against CSRF attacks.

Example of How to Prevent CSRF Attacks in Practice with Code that Uses a PHP Forms Generation Library Plugin to Generate Protection Tokens

The PHP Forms Generation package simplifies the creation, generation, and validation of forms with any input.

It has plug-ins that allow developers to create new form inputs that implement behaviors not available in the form inputs defined by the HTML specification standard documents.

One of those plug-ins provided by the PHP forms generation package is the secure_submit plug-in. this plug-in extends the behavior of the standard HTML form submit input to generate a hidden form input to pass a security token to protect against CSRF attacks.

The package comes with an example script named test_secure_submit.php that you can use to adapt and use in your applications.

If you use an MVC design pattern to implement your application, you may need to separate the form definition and processing to your controller code and the form output to your view code.

The test_secure_submit.php script does it all in a single script to make it simpler for those who prefer a more traditional PHP development approach that uses a single script to define, process, and display the form.

Let me comment on how the test_secure_submit.php script works:

First, let's include the forms class and the form_secure_submit_class scripts. If you use PHP composer to install this package, you must include the vendor/autoload.php script.

    require('forms.php');
    require('form_secure_submit.php');

Here you define a string that is a secret key. The form_secure_submit class will use this secret key to encrypt details of the security token that will be passed in a hidden input. This security token will be verified to avoid an eventual CSRF attack.

    $key="change this to a really secret key string";

To make this more secure, you can assign a PHP session variable to a random string, so the secret key can be different for each user. This will make the secret key even hard to guess an attacker.

Keep in mind that in more recent versions of PHP, there are more secure ways to generate random strings. I keep this example code using the following way to generate random strings because it still works in older PHP versions that you may still be using.

If you are using PHP 8.2 or later, please change the code that generates the random string to use the more modern PHP functions that generate random strings, like using the \Random\Randomizer class.

If you use PHP 7.3 or later, you may also want to use the session_set_cookie_params function to set the SameSite cookie property of PHP session cookies. While this is more secure, remember that SameSite cookie properties are not supported in older browsers.

    if(!IsSet($_SESSION['CSRF_TOKEN_KEY']))
    {
         $_SESSION['CSRF_TOKEN_KEY'] = hash('ripemd160', rand(0, getrandmax()));
    }
    $key = $_SESSION['CSRF_TOKEN_KEY'];

Letīs define the form and its input fields:

    $form = new form_class;
    $form->ID = 'secure_form';
    $form->METHOD = 'POST';

Set the form ACTION property to a specific URL.

    $form->ACTION = '?';

Define the secure form input using similar properties as if it is regularly submitted information by setting the TYPE property to custom, the CustomClass to form_secure_submit_class, and the Key property to the secret key value defined above.

    $error = $form->AddInput(array(
        'TYPE'=>'custom',
        'VALUE'=>'Secure submit',
        'ID'=>'secure_submit',
        'NAME'=>'secure_submit',
        'CustomClass'=>'form_secure_submit_class',
        'Key'=>$key,

The ExpireTime property defines the period that the CSRF token will be valid. If the user takes more time to submit the form, the token will be ignored as if the user did not click the submit input.

In this example, we set it to 300 seconds. That is 5 minutes. You may want to put it to a lower value for sensitive applications that deal with people's health or money.

        'ExpiryTime'=>300
    ));

If your form has more fields, define them here.

Form definition errors should not happen, but it is always good to check errors and process them in real applications to detect bugs as soon as possible.

    if(strlen($error))
        die("Error: ".$error);

Let's check whether the form was submitted and the CSRF token is valid.

    $submitted=$form->WasSubmitted('secure_submit');
    $form->LoadInputValues($submitted);
    $verify=array();
    if($submitted)
    {

If the form was submitted and is valid, let's validate any other fields you may have in the state.

        if(strlen($error_message=$form->Validate($verify))==0)
        {

The form is valid. The script is ready to process the form.

            $doit=1;
        }
        else
        {

The form validation did not pass. Let's get the main validation error message.

            $doit=0;
            $error_message=HtmlEntities($error_message);
        }
    }
    else
    {

The form was not submitted, or the CSRF token is not valid. Let's show the record to the user.

        $error_message='';
        $doit=0;
    }

    if(!$doit)
    {

The form is going to be displayed to the user. Let's make the forms class generate JavaScript code to give the input focus to the secure_submit input to make it easy for the user to submit the form just by hitting the Enter key.

You may want to remove this code if you do not want to give the keyboard focus to the secure_submit button. If you give the keyboard focus to another input, change the $focus variable to the ID property value of that input.

        $focus='secure_submit';
        $form->ConnectFormToInput($focus, 'ONLOAD', 'Focus', array());
    }

    $onload=HtmlSpecialChars($form->PageLoad());

?>
<html>
<head>
<title>Test for Manuel Lemos' PHP form class secure submit button</title>
</head>

Ensure the body tag has the onload tag value set to the value of the load variable. 

<body onload="<?php echo $onload; ?>">
<h1>Test for Manuel Lemos' PHP form class secure submit button</h1>
<?php
    if($doit)
    {

If the form was submitted and is valid, do something practical to process the form values.

?>
<h2>The form was submitted securely!</h2>
<?php
    }
  else
  {

If the form needs to be displayed, start capturing the output of this PHP script so that the forms class can combine the output of the script HTML with the HTML the class will generate for the form inputs.

        $form->StartLayoutCapture();

If the CSRF token expires or is invalid, you may want to tell the user to submit the form again.

        if(strlen($error=$form->GetInputProperty('secure_submit', 'Expired', $expired))==0
        && $expired)
        {
?>
<h2>The form submission expired. Please submit the form again.</h2>
<?php
        }
?>

Define the output of the form inputs. You can combine the form inputs with HTML templates that you want to use to define the layout of the form inputs according to your site style.

<?php
        $form->AddInputPart('secure_submit');
?>

When you complete the form output submission, you can request that the forms class output the HTML for the inputs.

<?php
        $form->EndLayoutCapture();

        $form->DisplayOutput();
    }
?>
</body>
</html>

How the HTML of the Form Will Look Using the CSRF Protection Token

If, for some reason, you prefer to use your own forms generation code instead of the forms package, take a look at the following HTML code to learn about how the HTML code will look like:

<html>
<head>
<title>Test for Manuel Lemos' PHP form class secure submit button</title>
</head>

Here the onload sets the keyboard focus to the secure_submit form submit input.

<body onload="if(document.getElementById('secure_form')['p_secure_submit_submit'].focus) document.getElementById('secure_form')['p_secure_submit_submit'].focus()" bgcolor="#cccccc">
<h1>Test for Manuel Lemos' PHP form class secure submit button</h1>
<form method="post" action="?" id="secure_form">

Here is the hidden input with an encrypted version of the CSRF protection token. It also contains the expiry time. The integrity of these values will be verified on the server side by the form_secure_submit_class.

If you implement your own CSRF token protection, ensure integrity verification to implement the CSRF protection robustly.

<input type="hidden" name="p_secure_submit_validation" value="kGBK36NZduHqidbsU4nTOQ==:c31674452345" id="p_secure_submit_validation" />
<input type="submit" name="secure_submit" value="Secure submit" id="p_secure_submit_submit" />
</form>
</body>
</html>

How to Download or Install the PHP Forms Generation Package Using PHP Composer

We recommend installing the forms generation package using PHP composer in your projects because it will generate a script named vendor/autoload.php that defines class autoloading code.

This means that you can include the file vendor/autoload.php in your projects, and any classes your project needs will be automatically loaded from the respective script files the first time each class is used.

Still, suppose you find the usage of PHP composer too complicated. In that case, you can use a more traditional approach of downloading the package code and adding any included statements at the beginning of your scripts to load all the classes you need.

You may find instructions to install this PHP forms generation package on the package page. The instructions will provide the information you need to install the package from the PHP Classes site composer repository with a few lines to add to your composer.json file.

Use the Install with Composer button to make the Composer configuration lines appear.

Alternatively, you may download a ZIP or .tar.gz package with all the package files from the same page.




You need to be a registered user or login to post a comment

1,615,513 PHP developers registered to the PHP Classes site.
Be One of Us!

Login Immediately with your account on:



Comments:

1. Great tutorial - Terry Woody (2023-01-24 04:14)
csrf tutorial... - 1 reply
Read the whole comment and replies



  Post a comment Post a comment   See comments See comments (2)   Trackbacks (0)  
  All package blogs All package blogs   PHP Forms Class with HTML Generator and JavaScript Validation PHP Forms Class with HTML Generator and JavaScript Validation   Blog PHP Forms Class with HTML Generator and JavaScript Validation package blog   RSS 1.0 feed RSS 2.0 feed   Blog Modern PHP CSRF Prote...