Form protection tool

Changed in version 12.1

Before TYPO3 v12.1, the FormProtectionFactory provided only static methods to get the concrete form protection implementation. Since TYPO3 v12.1, the FormProtectionFactory can be instantiated and therefore injected into the constructor. The static methods are deprecated and will be removed in TYPO3 v13.

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!