CSRF

Render the token

A CSRF token is a secret value tied to the visitor session and to one form purpose such as contact. The template prints it into a hidden field so the browser returns it on submit.

Generate the value in the controller with the token manager and pass it to the template. Use a distinct token id per form so a token from one page cannot unlock another.

namespace App\Controller; use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface; class FormController { public function form(CsrfTokenManagerInterface $tokens): string { return $tokens->getToken("contact")->getValue(); } }

Check it

Validation compares the submitted value with the expected one stored for the session. The manager method isCsrfTokenValid returns true only on an exact match.

Build a token object from the token id and the sent string, then branch on the result. Reject the request with an error page when the check fails.

namespace App\Controller; use Symfony\Component\Security\Csrf\CsrfToken; use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface; class FormController { public function save(string $sent, CsrfTokenManagerInterface $tokens): bool { return $tokens->isCsrfTokenValid(new CsrfToken("contact", $sent)); } }

Forms include it

Classic HTML forms carry the token in a hidden input next to the visible fields. The browser sends it automatically with the POST body, so no extra script is required.

Read the posted value from the request body and validate it before touching the database. Keep the token id identical in the template and in the controller check.

namespace App\Controller; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Security\Csrf\CsrfToken; use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface; class FormController { public function save(Request $request, CsrfTokenManagerInterface $tokens): bool { $sent = $request->request->get("token", ""); return $tokens->isCsrfTokenValid(new CsrfToken("contact", $sent)); } }

Send in headers

Script driven requests cannot rely on hidden inputs, so they send the token in a custom header instead. The page script reads the token from a meta tag and attaches it to each call.

Read the header value on the server with the same token id and validate it like a form field. Reject missing headers early because they signal a broken client or a forged call.

namespace App\Controller; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Security\Csrf\CsrfToken; use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface; class ApiController { public function save(Request $request, CsrfTokenManagerInterface $tokens): bool { $sent = $request->headers->get("X-CSRF-TOKEN", ""); return $tokens->isCsrfTokenValid(new CsrfToken("contact", $sent)); } }

Next: Translations

Article author: Arthur Isaev