How to define CSRF token in ajax call in Cakephp 3. Also How CSRF can be off for some ajax requests
Asked Answered
K

3

7

In Cakephp3 when the Csrf component is enabled. How I can use it in ajax call. In this beforeSend parameter of ajax csrf token is set in header. What is the value of csrfToken. As it gives error

csrfToken is not defined

beforeSend: function(xhr){
    xhr.setRequestHeader('X-CSRF-Token', csrfToken);
},

Also how can I disable Csrf component for some ajax calls.

Kb answered 9/6, 2017 at 9:58 Comment(1)
A
7

The CSRF component writes the current token to the request parameters as _csrfToken, you can get it via the request object's param() method (or getParam() as of CakePHP 3.4):

beforeSend: function(xhr){
    xhr.setRequestHeader(
        'X-CSRF-Token',
        <?= json_encode($this->request->param('_csrfToken')); ?>
    );
},

To make the token available to all your scripts, you can for example make it globally available as variable in your layout template:

<script>
var csrfToken = <?= json_encode($this->request->param('_csrfToken')) ?>;
// ...
<script>

You can then easily use it in all your AJAX requests:

setRequestHeader('X-CSRF-Token', csrfToken);

The CSRF component can be disabled by removing it from the controllers event manager. You'll have to figure on what condition you'd need to do that, for example for a specific action, like this:

public function beforeFilter(\Cake\Event\Event $event)
{
    parent::beforeFilter($event);

    if ($this->request->param('action') === 'actionXyz') {
        $this->eventManager()->off($this->Csrf);
    }
}

If you're using the CSRF middleware, then the token is still available as a request parameter named _csrfToken, disabling the middleware however works differently, see for example Cakephp 3.5.6 disable CSRF Middleware for controller

See also

Apache answered 9/6, 2017 at 10:25 Comment(4)
Thanks for the reply. :)Kb
This could be a quick fix but i think, we should not disable CSRF security component. It is there for a reason. In my case, serialising the form before ajax worked fine. I think, You don't need to use beforesend as well.Grantor
@Grantor That's IMHO a bit to blanket, there are many situations in which using headers is the correct/sensible approach, there might for example be no form at all, ie nothing to serialize, or the form might not have been created via the form helper, so there wouldn't be any token in it. And while using CSRF protection is advised, there are situations in which disabling it is totally fine, sometimes even required, for example in case of an API (or similar endpoints that accept "external", non-form requests) that uses auth mechanisms (or none at all) that aren't prone to CSRF to begin with.Apache
@Apache Yes, completely agree. It depends on the situation.Grantor
L
3

Every form has a hidden _csrfToken field that's automatically added when you have enabled the Csrf component. Now you can easily get the token of this field by jquery like $('[name="_csrfToken"]').val().

A ajax call will look like this:

$.ajax({
   url: 'someUrl',
   headers : {
      'X-CSRF-Token': $('[name="_csrfToken"]').val()
   },
   type: 'post',
   ...
});
Laager answered 21/7, 2018 at 12:3 Comment(0)
G
0

CakePHP 3

Please do not unlock fields/disable CSRF security component for any particular action. This is important for the form security.

for those who are getting "The request has been black-holed." ,"form tampered error", "you are not authorized to access that location." or "unexpected field in POST data". It is mainly due to the CSRF component working as expected.

Disabling or modifying it is not a solution. Instead of disabling, please follow the right approach. In above case, please try serializing the form and that should do the magic.

var el = $("#xyzForm");

var ajaxTPCalls = el.serializeArray();
  $.ajax({
                            type: el.attr('method'),
                            async: true,
                            url:  el.attr('action'),
                            data: ajaxTPCalls,
                            dataType: "json",
                            cache: false,
                            success: function (data) {

                                toastr.success(data.message, data.title);
                            },
                            error: function (jqXHR) {
                                if (jqXHR.status == 403) {
                                    $("body").html(jqXHR.responseText);
                                }
                            }
                        });

This way you do not disable CSRF or unlock any field.

Grantor answered 20/9, 2018 at 11:32 Comment(1)
Frustratingly, even if I use the serialize method, I still end up with _Token was not found in request data. The form's created with FormHelper, and has all the right fields in it...Wadmal

© 2022 - 2024 — McMap. All rights reserved.