CSRF Validation in AJAX With Symfony
Goal[edit]
From a Symfony page, submit POST data including a CSRF token using jQuery, then validate that CSRF token in the controller that handles the request.
Template[edit]
csrf_token() is a built-in Twig template function with Symfony. Normally it's used with forms.
<div class="hidden" id="csrf-token" data-token="{{ csrf_token('') }}"><!-- --></div>
JavaScript/jQuery[edit]
- Bind an
ajaxSendhandler to the document that will pass along the csrf token in the request headers for every AJAX call.
$(document).bind('ajaxSend', function(elm, xhr, s){
if (s.type==="POST") {
var csrf_token = $(lclSettings.selectors.csrf).data('token');
xhr.setRequestHeader('X-CSRF-Token', csrf_token);
}
});
Symfony controller[edit]
Retrieve the CSRF token from the request headers and validate it in the controller.[1][2]
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
// ...
/**
* @Route("/update/completed/{id}", name="_updated_completed")
* @ParamConverter("tutorial", class="AppBundle:Tutorial")
* @Method("POST")
*/
public function updateCompletedAction(Request $request, Tutorial $tutorial)
{
// get the csrf token from the request header
$csrf_token = $request->headers->get('x_csrf_token');
// get the object to validate the token
$csrf = $this->get('form.csrf_provider');
// validation
if ($csrf->isCsrfTokenValid('', $csrf_token)) {
// Handle the request and create a json string for the response
}
else {
// Invalid token
$response = new Response(json_encode(array(
'error' => 'Invalid request.'
)));
}
// return JSON response
$response->headers->set('Content-Type', 'application/json');
return $response;
}
- The
@ParamConverterannotation causes the id value to automatically be converted into an entity object, but it doesn't have any direct impact on the CSRF logic. - The
@Methodannotation causes only requests using the POST method to be matched. This also doesn't directly impact the CSRF, but it does provide a layer of protection from inspecting the requests. - Get the token string from the headers.
- Get a Symfony CSRF provider object to do the validation.
- Validate the token and only perform the action if the token is valid.
- Returns a JSON string to the script that called this action.
Notes[edit]
- ↑ How Can I Check Whether The Supplied CSRF Token Is Invalid In Symfony2, Stackoverflow
- ↑ Symfony CSRF and AJAX, Stackoverflow