Form protection tool

The TYPO3 Core provides a generic way of protecting forms against cross-site request forgery (CSRF).

For each form in the backend/frontend (or link that changes some data), create a token and insert it as a hidden form element. The name of the form element does not matter; you only need it to get the form token for verifying it.

Examples

EXT:my_extension/Classes/Controller/FormProtectionExample.php
<?php

declare(strict_types=1);

namespace MyVendor\MyExtension\Controller;

use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use TYPO3\CMS\Core\FormProtection\FormProtectionFactory;

final class FormProtectionExample
{
    public function __construct(
        private readonly FormProtectionFactory $formProtectionFactory,
    ) {}

    public function handleRequest(ServerRequestInterface $request): ResponseInterface
    {
        $formProtection = $this->formProtectionFactory->createFromRequest($request);

        $formToken = $formProtection->generateToken('BE user setup', 'edit');

        $content = '<input type="hidden" name="formToken" value="' . $formToken . '">';

        // ... some more logic ...
    }
}
Copied!

The three parameters of the generateToken() method:

  • $formName
  • $action (optional)
  • $formInstanceName (optional)

can be arbitrary strings, but they should make the form token as specific as possible. For different forms (for example, BE user setup and editing a tt_content record) or different records (with different UIDs) from the same table, those values should be different.

For editing a tt_content record, the call could look like this:

EXT:my_extension/Classes/Controller/FormProtectionExample.php (Excerpt)
$formToken = $formProtection->generateToken('tt_content', 'edit', (string)$uid);
Copied!

When processing the data that has been submitted by the form, you can check that the form token is valid like this:

EXT:my_extension/Classes/Controller/FormProtectionExample.php (Excerpt)
if ($dataHasBeenSubmitted &&
    $formProtection->validateToken(
        $request->getParsedBody()['formToken'] ?? '',
        'BE user setup',
        'edit'
    ) ) {
    // process the data
} else {
    // No need to do anything here, as the backend form protection will
    // create a flash message for an invalid token
}
Copied!

As it is recommended to use FormProtectionFactory->createForRequest() to auto-detect which type is needed, one can also create a specific type directly:

EXT:my_extension/Classes/Controller/FormProtectionExample.php (Excerpt)
// For backend
$formProtection = $this->formProtectionFactory->createFromType('backend');

// For frontend
$formProtection = $this->formProtectionFactory->createFromType('frontend');
Copied!