The default CSRF prevention in Symfony is form-based (which happens automatically if you use the provided form builder). However, for AJAX requests it is tedious to manually attach the CSRF token to each HTTP request and then check it manually for each request.
A good approach would be to embed the token as an HTTP header, as suggested in the comments of this question. jQuery can then be configured to include this header on each request as described here.
My question is how best to handle this within Symfony? I can include the CSRF token in each page using Twig, but how do I check incoming requests to ensure that each request contains a valid token in the header?
I'm aware of how to access the HTTP headers using $request->headers->get('X-CSRF-Token')
from within a controller, but that still means having to perform this check within each controller individually. Whereabouts should I add this check so that it is caught as early as possible?
Im not 100% sure on this but I would use a BaseFormType and extend that.
Your base form type will use the form event PRE_SET_DATA listener and take the request look for the header then populate that header into the _token
field.
//FormType
class BaseFormType extends AbstractType
{
protected $request;
public function __construct(Request $request)
{
$this->request = $request;
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$request = $this->request;
$builder->addEventListener(FormEvents::PRE_SET_DATA, function(FormEvent $event) use($request){
$token = $request->headers->get('X-CSRF-Token');
if($token){
$form = $event->getForm();
$form->get('_token')->setData($token);
}
}
}
}
Then all of your FormTypes will extend this:
//YourCustomFormType
class YourCustomFormType extends BaseFormType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
parent::buildForm();
$form->add('name');
}
}
Then your controller action
//Controller
public function sayMyNameAction(Request $request)
{
$name = new Name();
$form = $this->createForm(new YourCustomFormType($request),$name);
if($request->isMethod('POST'))
{
$form->handleRequest($request);
if($form->isValid()){
return new JsonResponse(array('say':$name->getName()));
}
}
}
Or something along those lines.