11

I have created a Symfony2 form and bound it to the Request. I need to explicitly ensure whether the CSRF token is valid/invalid before proceeding with the rest of the form.

$form['_token']->isValid() throws OutOfBoundsException with message "Child _token does not exist."

I can still verify that the rendered form contains _token field. In case that CSRF value is invalid, $form->isValid() returns false.

What am I missing here?


Update 1:

Controller (partial):

private function buildTestForm() {
    $form = $this->createFormBuilder()
            ->add('name','text')
            ->getForm();
    return $form;
}

/**
 * @Route("/test/show_form", name="test.form.show")
 * @Method("GET")
 */
public function showFormTest()
{
    $form = $this->buildTestForm();
    return $this->render('TestBundle::form_test.html.twig', array('form' => $form->createView()));
}

/**
 * @Route("/test/submit_form", name="test.form.submit")
 * @Method("POST")
 */
public function formTest()
{
    $form = $this->buildTestForm();
    $form->bind($this->getRequest());
    if ($form['_token']->isValid()) {
        return new Response('_token is valid');
    } else {
        return new Response('_token is invalid');
    }
}

Template

{# Twig template #}
<form action="{{ path('test.form.submit') }}" method="post" {{ form_enctype(form) }}>
    {{ form_widget(form) }}
    <input type="submit" name="go" value="Test Form" />
</form>
4
  • you are missing at least the form code. Commented Feb 23, 2013 at 19:50
  • @mpm, true - now added full code to reproduce the problem. Commented Feb 23, 2013 at 21:56
  • CSRF Token validation is done automatically symfony.com/doc/current/book/forms.html#csrf-protection Commented Feb 23, 2013 at 22:00
  • @artworkadシ yes, I am aware of that. The problem is that I need to react differently in case that _token is invalid. That is why I need to check it explicitly. Commented Feb 23, 2013 at 22:10

4 Answers 4

11

There is no documented way to check csrf token manually. Symfony automatically validates the presence and accuracy of this token. http://symfony.com/doc/current/book/forms.html#csrf-protection

However there is a csrf provider:

http://api.symfony.com/2.0/Symfony/Component/Form/Extension/Csrf/CsrfProvider/SessionCsrfProvider.html

and

http://api.symfony.com/master/Symfony/Component/Form/Extension/Csrf/CsrfProvider/DefaultCsrfProvider.html

Marks classes able to provide CSRF protection You can generate a CSRF token by using the method generateCsrfToken(). To this method you should pass a value that is unique to the page that should be secured against CSRF attacks. This value doesn't necessarily have to be secret. Implementations of this interface are responsible for adding more secret information.

If you want to secure a form submission against CSRF attacks, you could supply an "intention" string. This way you make sure that the form can only be bound to pages that are designed to handle the form, that is, that use the same intention string to validate the CSRF token with isCsrfTokenValid().

You can retrieve the provider like this

$csrf = $this->get('form.csrf_provider');

use can then use

public Boolean isCsrfTokenValid(string $intention, string $token)

Validates a CSRF token.

Parameters string $intention The intention used when generating the CSRF token string $token The token supplied by the browser

Return Value Boolean Whether the token supplied by the browser is correct

You need to finde out the intention string used by your form.

Some interesting posts on SO:

Symfony CSRF and Ajax

Symfony2 links with CSRF token

Sign up to request clarification or add additional context in comments.

1 Comment

Deprecated since Symfony 2.4, use security.csrf.token_manager instead. api.symfony.com/2.7/Symfony/Component/Security/Csrf/…
11

In addition to artworkad, you can specify an intention:

Twig:

<form method="post">
    <input type="text" name="form_name[field]" value="" />
    <input type="hidden" name="_csrf_token" value="{{ csrf_token('form_name') }}">
</form>

PHP:

$token = $request->request->get('_csrf_token');
$csrf_token = new CsrfToken('form_name', $token);
var_dump($this->get('security.csrf.token_manager')->isTokenValid($csrf_token));

Or not:

Twig:

<form method="post">
    <input type="text" name="field" value="" />
    <input type="hidden" name="_csrf_token" value="{{ csrf_token('') }}">
</form>

PHP:

$token = $request->request->get('_csrf_token');
$csrf_token = new CsrfToken('', $token);
var_dump($this->get('security.csrf.token_manager')->isTokenValid($csrf_token));

2 Comments

Should ajax request regenerate the token?
You can regenerate the token in ajax and update your input value : public function tokenAction($intention) { $csrf = $this->get('security.csrf.token_manager'); $token = $csrf->refreshToken($intention); return new Response($token); }
1

Another way (if you prefer DI) to validate token outside of controller (e.g. in listener):

/** @var Symfony\Component\Security\Csrf\CsrfTokenManagerInterface */
private $csrfTokenManager;

public function __construct(CsrfTokenManagerInterface $csrfTokenManager) 
{
    $this->csrfTokenManager = $csrfTokenManager;
}

public function isValid(): bool
{
    return $this->csrfTokenManager->isTokenValid(
            new CsrfToken('authenticate', $request->getRequest()->headers->get('X-CSRF-TOKEN'))
        );
}

Comments

0

I am not sure of this but have you tried the validator on an unmapped field:

$violations = $this->get('validator')->validatePropertyValue($entity, '_token', $form['_token']);
if (count($violations)) {
    // the property value is not valid
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.