CSRF protection in forms

Summary

This page explains how to add CSRF protection to a form by creating a CSRF field, validating it in the InputFilter, rendering it in the template, and testing the result.

Details

A Cross-Site Request Forgery (CSRF) attack is a type of security vulnerability that tricks a user into performing actions on a web application in which they are authenticated, without their knowledge or consent.

Web applications can protect users against these types of attacks by implementing CSRF tokens in their forms, which are known only to the application that generated them and must be included when submitting forms. With each visit, a new CSRF token is added to the form, so tokens are not reusable between forms. Missing to provide a valid CSRF token will result in a form validation error.

Implement CSRF protection

Implementing CSRF protection requires three steps:

Create field

Open the form's PHP class and append the following code to the method that initializes the fields (usually init):

$this->add(
    (new \Laminas\Form\Element\Csrf('exampleCsrf'))
        ->setOptions([
            'csrf_options' => ['timeout' => 3600, 'session' => new Container()],
        ])
        ->setAttribute('required', true)
);

where exampleCsrf should be a suggestive name that describes the purpose of the field (example: forgotPasswordCsrf).

Validate field

Open the InputFilter that validates the form fields and append the following code to the method that initializes the fields (usually init):

$this->add(new \Admin\App\InputFilter\Input\CsrfInput('exampleCsrf'));

where exampleCsrf must match the CSRF field's name in the form.

Remember to modify both occurrences in this file.

Make sure that you validate the form using its isValid method in the handler/controller where it is submitted.

Render field

Open the template that renders your form and add the following code somewhere between the form's opening and closing tags:

{{ formElement(form.get('exampleCsrf')) }}

Test the implementation

Access your form from the browser and view its source. You should see a new hidden field, called exampleCsrf (or however you named it). After filling out the form, submitting it should work as before.

To make sure that the new CSRF field works as expected, you can inspect the form using your browser's Developer tools and modify its value in any way. Submitting a filled-out form should result in a validation error:

This field is required and cannot be empty.

Timeout

Note the timeout option in your PHP form's exampleCsrf field, with its default value set to 3600. This represents the value in seconds for how long the token is valid. Submitting a form that has been rendered for longer than this value will result in a validation error:

Invalid CSRF.

You can modify the value of timeout in each form, but the default value should work in most cases.

FAQ

Q: What is a CSRF token used for?

A: It protects users against Cross-Site Request Forgery attacks by ensuring that a form submission was generated by the application itself and not forged by an attacker.

Q: What steps are required to implement CSRF protection?

A: You need to create the CSRF field in the form, validate it in the InputFilter, and render it in the template between the form's opening and closing tags.

Q: What happens if the CSRF token is missing or invalid?

A: The form submission fails validation, showing an error such as "This field is required and cannot be empty" or "Invalid CSRF."

Q: How long is a CSRF token valid?

A: By default, a token is valid for 3600 seconds, controlled by the timeout option, which you can adjust per form.