1

I have two separate projects: UI(AngularJS) and Server(Symfony2).

I need to send cross-domain PUT request from AngularJS application to the Symfony2 app.

In the Symfony controller I passed $request to the form->handleRequest(); and debug showed me, that form using this way is not submitted.

So, next I tried to pass $request to the form->submit() and got error "Invalid CSRF Token".

  1. How can I correctly process cross-domain data via Symfony forms? I've read that passing $request to the submit() method is depricated.
  2. How can I pass CSRF token to the form if I send it from UI via headers ? (I add csrf field to the request but it not processing at back-end)

EDITED: Currently I see that issue is related to CSRF token. No matter how I sending this token from UI, it's not processed on back-end and I always get "Invalid CSRF token" error.

I tried to add _token field directly to json object and set field name to _token via csrf_field_name option into my formtype class.

I also tried to pass json_decode($request->getContent(), true) to my form submit() method, but after debugging I see, that submittedData is changing in next code :

// Symfony/Component/Form/Form.php, submit() method

if ($dispatcher->hasListeners(FormEvents::PRE_SUBMIT)) {
    // here submittedData has csrf _token key/value
    $event = new FormEvent($this, $submittedData);
    $dispatcher->dispatch(FormEvents::PRE_SUBMIT, $event);
    $submittedData = $event->getData();
    // now submittedData without _token key/value
}

EDITED2: more details. CsrfValidationListener that using by Symfony Form component call $this->tokenManager->isTokenValid(new CsrfToken($this->tokenId, $data[$this->fieldName])) and this return false, the issue in next code:

// Symfony/Component/Security/Csrf/CsrfTokenManager.php

public function isTokenValid(CsrfToken $token)
    {
        if (!$this->storage->hasToken($token->getId())) {
            return false;
        }

        return StringUtils::equals($this->storage->getToken($token->getId()), $token->getValue());
    }

It seems csrf token is stored into session, so isTokenValid() method return false.

I continue to debug.

EDITED3:

as I can see, session is empty on calling $this->storage->hasToken($token->getId()) from CsrfTokenManager.php.

This is very strange, because I generate csrf token from my controller in next way:

$csrfToken = $this->get('security.csrf.token_manager')->refreshToken('Symfony');

And as I can see, refreshToken() method save csrf token into db:

// Csrf/CsrfTokenManager.php
public function refreshToken($tokenId)
    {
        $value = $this->generator->generateToken();

        $this->storage->setToken($tokenId, $value);

        return new CsrfToken($tokenId, $value);
    }
// Csrf/TokenStorage/SessionTokenStorage.php
public function setToken($tokenId, $token)
    {
        if (!$this->session->isStarted()) {
            $this->session->start();
        }

        $this->session->set($this->namespace.'/'.$tokenId, (string) $token);
    }

But when I send data to the form, $this->tokenManager->isTokenValid(new CsrfToken($this->tokenId, $data[$this->fieldName])) that calls from preSubmit() method of CsrfValidationListener return empty session.

just in case I add my security.yml settings, maybe I have missed something:

main:
    pattern: ^/(?!login).+
    stateless: true
    simple_preauth:
        authenticator: app_bundle.api_key_authenticator
    provider: api_key_user_provider
    anonymous: ~
    logout: ~

login:
    pattern: ^/login
    stateless: false
    simple_preauth:
        authenticator: app_bundle.email_password_authenticator
    provider: email_user_provider
    anonymous: ~

Notice: I generate csrf-token under login firewall and try to access it from main firewall!

But I also tried to generate csrf-token in the same firewall. Nothing changed.

EDITED4:

I have configured custom session dir for tracking session creation. So, I can see, that on login I have session with all attributes, but when I doing PUT request, I notice that new session file is created and it contains something like this:

_sf2_attributes|a:0:{}_sf2_flashes|a:0:{}_sf2_meta|a:3:{s:1:"u";i:1449700968;s:1:"c";i:1449700968;s:1:"l";s:1:"0";}

Just empty session.

Sergio Ivanuzzo
  • 1,466
  • 4
  • 21
  • 48

1 Answers1

0

So, I have found the reason of csrf error behavior.

When csrf token created, it stored into session. Because of my api firewall is stateless, it can't store csrf token. And also on each authentication session is drop and has only current authentication token.

Properly configured CORS help to protect from csrf attack.

See also this answer about CORS headers.

Hope, this will be helpful for somebody.

Community
  • 1
  • 1
Sergio Ivanuzzo
  • 1,466
  • 4
  • 21
  • 48