CSRF Validation in AJAX With Symfony: Difference between revisions

From Littledamien Wiki
Jump to navigation Jump to search
(Created page with "Category:Symfony Category:PHP Category:jQuery Category:JavaScript Category:Web Development == Goal == From a Symfony page, submit POST data including a CS...")
 
No edit summary
 
Line 66: Line 66:
return $response;
return $response;
}
}
</syntaxhighlighter>
</syntaxhighlight>


* The `@ParamConverter` annotation 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 `@ParamConverter` annotation causes the id value to automatically be converted into an entity object, but it doesn't have any direct impact on the CSRF logic.

Latest revision as of 20:00, 10 February 2015

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 ajaxSend handler 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 @ParamConverter annotation 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 @Method annotation 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]