Reputation: 651
I am trying to implement some ajax functionality in my Symfony 2 project. Using jquery's $.post I want to send some data back to my controller. However, when I just POST the data no CSRF protection is in place, as symfony's csrf protection only seems to apply to forms.
What would be a pretty straightforward way to implement this?
When using forms I can just do $form->isValid() to find out whether or not the CSRF token passes. I am currently placing everything I want to POST in a form and then posting that. Which basically means I am only using that form to implement CSRF protection, which seems hacky.
Upvotes: 27
Views: 22538
Reputation: 9846
In Symfony 4+ you can use dependency injection right into your controller or action or wherever, for example, if you are submitting a form and wish to refresh the token of the same form, the $tokenId
is the FQDN of the form type class:
namespace App\Controller;
use App\Form\MyFormType;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface;
class MyController extends AbstractController
{
public function submit(CsrfTokenManagerInterface $tokenManager): JsonResponse
{
// ...
$token = $tokenManager->refreshToken(MyFormType::class);
return new JsonResponse(['token' => $token->getValue()]);
}
}
And in your JavaScript you can update the existing token <input>
.
const token = document.getElementById('_token');
fetch(url, opts)
.then(resp => resp.json())
.then(response => {
if (response.token) {
token.value = response.token;
}
});
Upvotes: 0
Reputation: 17976
In Symfony2 CSRF token is based on session by default. If you want to generate it, you just have to get this service and call generation method:
//Symfony\Component\Form\Extension\Csrf\CsrfProvider\SessionCsrfProvider by default
$csrf = $this->get('form.csrf_provider');
//Intention should be empty string, if you did not define it in parameters
$token = $csrf->generateCsrfToken($intention);
return new Response($token);
This question might be useful for you
Upvotes: 26
Reputation: 3048
I had this problem, intermittently. Turned out it was not due to my ajax, but because Silex gives you a deprecated DefaultCsrfProvider
which uses the session ID itself as part of the token, and I change the ID randomly for security. Instead, explicitly telling it to use the new CsrfTokenManager
fixes it, since that one generates a token and stores it in the session, such that the session ID can change without affecting the validity of the token.
/** Use a CSRF provider that does not depend on the session ID being constant. We change the session ID randomly */
$app['form.csrf_provider'] = $app->share(function ($app) {
$storage = new Symfony\Component\Security\Csrf\TokenStorage\SessionTokenStorage($app['session']);
return new Symfony\Component\Security\Csrf\CsrfTokenManager(null, $storage);
});
Upvotes: 2
Reputation: 395
You should try this snippet. Symfony form should generate special _csrf_token that should be send with post request. Without this value security alert will be raised.
Of course #targetForm should be replaced by form id and /endpoint by target ajax url
$('#targetForm').bind('submit', function(e) {
e.preventDefault();
var data = $(this).serialize();
$.post('/endpoint', data, function(data) {
// some logic here
});
});
Upvotes: 1