Search code examples
jquerysymfonycsrf-protectionsymfony-3.4

Protect jquery Ajax Requests from CSRF attacks via CSRF token on Symfony 3.4


I have the following Symfony Controller:

namespace AppBundle\Controller

use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\JsonResponse;

class MyController extends Controller
{
  /**
  * @Route("/form", name="get_form")
  * @Method("GET")
  */
  public function getFormAction(Request $request)
  {
    return $this->render('some_twig.html.twig');
  }

  /**
  * @Route("/form_submit", name="submit_form")
  * @Method("POST")
  */
  public function ajaxFormAction(Request $request)
  {
    if($request->isXmlHttpRequest()){
    return new JsonResponse(['data':"All midori"],JsonResponse::HTTP_OK);
    } else {
     return new JsonResponse(['data':"Echi, hentai, baka"],JsonResponse::HTTP_BAD_REQUEST);
    }
  }
}

The form is submitted via the following javascript code:

$("#someform").on('submit',function(e){
    e.preventDefault();

    var self=this; //To avoid Confusion using this
    var url=$(self).attr('action');

    $.ajax({
      'method': "POST",
      'url': url,
      'data': $(self).serialize(),
      'statusCode': {
        400: function(data,textStatus,jqXHR) {
          //Handle Error 400
        },
        500: function(data,textStatus,jqXHR){
         //Handle error 500
        }
      },
      'success':function(data){
        //DO some stuff
      }
    });
  })

But I have some trouble on how I can protect the Symfony Ajax method from CSRF attacks. When I try to protect via putting CSRF token to form when an aeero occurs eg. an exception thrown it renders my form junk.

Also leaving unprotected is not the best viable option. So how effectively can protect it?


Solution

  • Via expirementation the best viable option is to use the DunglasAngularCsrfBundle (look for documentation here). In your example place the following code into your security.yml:

    dunglas_angular_csrf:
      # Collection of patterns where to set the cookie
      cookie:
          set_on:
            - {route: ^get_form, methods: [GET]}
      secure:
        - {route: ^submit_form, methods:[POST]}
    

    Then modify the jquery code as follows:

    $("#someform").on('submit',function(e){
    e.preventDefault();
    
    var self=this; //To avoid Confusion using this
    var url=$(self).attr('action');
    
    $.ajax({
      'method': "POST",
      'url': url,
      'data': $(self).serialize(),
      'beforeSend': function(request) {
        request.setRequestHeader('X-XSRF-TOKEN', Cookies.get('XSRF-TOKEN'));
      },
      'statusCode': {
        400: function(data,textStatus,jqXHR) {
          //Handle Error 400
        },
        500: function(data,textStatus,jqXHR){
         //Handle error 500
        }
      },
      'success':function(data){
        //DO some stuff
      }
    });
    
    
    })
    

    I get the cookie value using the library js-cookie but you can use the methods mentioned in the following answer as well.

    The whole idea is to get the CSRF token via the XSRF-TOKEN (should be in caps instead of what the documentation says) and return the value via a custom http header X-XSRF-TOKEN (all should be caps as well).

    So you can kiss the vulnerability goodbye.