Unlockfield with Ajax

Hi

I use the securityComponent for all my form on all my website. I would like to use also the token field for ajax call.
I have an onchange() method on an input field that do an ajax request with jquery (actually in get put I can change it if needed). What is the best way to do that ? My goal is to prevent CRSF and avoid users that directly call the url without being on the page.

thanks

I usually add the CSRF Token as a header like shown bellow because it doesn’t matter from which form the CSRF Token comes from.

$.ajax({
  type: 'get',
  dataType: 'json',
  url: `${fetchUrl}`,
  headers: {
    'X-CSRF-Token': $('[name="_csrfToken"]').val(),
  },
  success(response) {
    // do something
  },
  error(jqXHR, textStatus, errorThrown) {
    // do something
  },
});

and to unlock those specific controller actions to be used without the _Token fields provided by the FormProtection component you will need to add this to your controller

public function beforeFilter(EventInterface $event)
{
    parent::beforeFilter($event);

    $this->FormProtection->setConfig('unlockedActions', [
        'ajaxAction1', 'ajaxAction2', 'ajaxAction3'
    ]);
}

FYI: The SecurityComponent is deprecated and will be removed in CakePHP 5 so I would recommend you switch to the FormProtection component for this kind of stuff.

But you can do the same unlocking configuration with the SecurityComponent as well:

public function beforeFilter(EventInterface $event)
{
    parent::beforeFilter($event);

    $this->Security->setConfig('unlockedActions', [
        'ajaxAction1', 'ajaxAction2', 'ajaxAction3'
    ]);
}

why unlock actions ? the goal is tha action use the token, so I don t understand. the doc says

" There may be cases where you want to disable form tampering prevention for an action (ex. AJAX requests). You may “unlock” these actions by listing them in $this->Security->unlockedActions in your beforeFilter() :"

so if I understand, I have to let it do his job, right ?

The FormProtection/Security Component basically adds a hash of all the fields CakePHP generates for a specific form.

So for a simple form like

echo $this->Form->create($entity);
echo $this->Form->control('name');
echo $this->Form->button('Add');
echo $this->Form->end();

your Form will get additional hidden inputs added to them (not only the _csrfToken

These contain a hash of all the fields which made up the current form which lives in the _Token[fields]

You can of course send that data via AJAX as well if you don’t change, add or remove any inputs from that specific form via JS.

But most of the time AJAX calls are made with dynamic input fields which PHP doesn’t know beforehand, therefore it can’t generate a hash of all the fields. Thats why you need to unlock that controller action to not check for FormProtection/Security Component validity.

You can mitigate simple “direct URL calls” via embeding that CSRF Token. The _Token fields are for FormProtection.

I understand that the job of verification of _token[*] has to be disabled. I was thinking it was pretty the same (both use token name ;)) (now I understand that token is for avoid change in the fields list while crsf is to avoid the vulnerability of the same name)

If I unlockFields =>the _csrfToken will be checked but not the token[field].

I thought the _csrfToken was change at form /page creation but realized that it is at login level (right?)? so once logged a user can do ajax call it without being on my page. (the initial goal). Maybe I have to generate a personal token on my page and check it on ajax call (this way I force the user to be on my page. Is that the best way ? does CakePhp has some security facilities for that.

again …thanks.

Just so you know: The CSRF Token gets set as a session cookie by the CsrfProtectionMiddleware (which you added in your src/Application.php) the first time a user does a request and does not already have a CSRF cookie. See CSRF Protection - 4.x

Therefore it is always the same for that current active session.

so The best ways (I know CORS policy but that’s not enough to me) is to do it by hands with something generated when page loaded and checked when ajax is call ?

I dont understand your question here, sorry

oh. sorry…The question is : what is the best way to protect an ajax url to be called only in ajax on the page where it should be called…in other words : avoid user to log and then call the ajax url (behind the scene : the ajax url retrieve some data from an external API that is not free and I don’t want user to use this url in an other context than the initial one)

What your are talking about is basically a CSRF attack which CakePHP already mitigates via its csrf token implementation.

But if that is not enough for you then you could create a custom middleware which checks the Origin HTTP Header for your specific ajax action and returns a e.g. 403 when it doesn’t match what you are expecting