Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

'_Token' was not found in request data in CakePHP3 after server migration

Tags:

php

cakephp

Edit:

A whole series of new insights gained after asking this question have taught me what the issue was, and it definitely did not have anything to do with the described server migration.

The two given answers show how to "fix" this for both CakePHP 2 and 3, though bear in mind this might pose a security risk. The CSRF component is an important security feature, and should not be disabled lightly.

Original question:

I migrated my CakePHP 3 project from XAMPP on my laptop to XAMPP on a server. Ever since when I activate the Security component, cake throws me an error. Here it is, directly from the Error log:

    2016-05-21 20:32:01 Error: [Cake\Controller\Exception\AuthSecurityException] '_Token' was not found in request data.
Request URL: /Users/addUser
Referer URL: http://localhost/users/add_user
Stack Trace:
#0 C:\xampp\htdocs\vendor\cakephp\cakephp\src\Controller\Component\SecurityComponent.php(324): Cake\Controller\Component\SecurityComponent->_validToken(Object(App\Controller\UsersController))
#1 C:\xampp\htdocs\vendor\cakephp\cakephp\src\Controller\Component\SecurityComponent.php(130): Cake\Controller\Component\SecurityComponent->_validatePost(Object(App\Controller\UsersController))
#2 C:\xampp\htdocs\vendor\cakephp\cakephp\src\Event\EventManager.php(386): Cake\Controller\Component\SecurityComponent->startup(Object(Cake\Event\Event))
#3 C:\xampp\htdocs\vendor\cakephp\cakephp\src\Event\EventManager.php(356): Cake\Event\EventManager->_callListener(Array, Object(Cake\Event\Event))
#4 C:\xampp\htdocs\vendor\cakephp\cakephp\src\Event\EventDispatcherTrait.php(78): Cake\Event\EventManager->dispatch(Object(Cake\Event\Event))
#5 C:\xampp\htdocs\vendor\cakephp\cakephp\src\Controller\Controller.php(495): Cake\Controller\Controller->dispatchEvent('Controller.star...')
#6 C:\xampp\htdocs\vendor\cakephp\cakephp\src\Routing\Dispatcher.php(109): Cake\Controller\Controller->startupProcess()
#7 C:\xampp\htdocs\vendor\cakephp\cakephp\src\Routing\Dispatcher.php(87): Cake\Routing\Dispatcher->_invoke(Object(App\Controller\UsersController))
#8 C:\xampp\htdocs\webroot\index.php(37): Cake\Routing\Dispatcher->dispatch(Object(Cake\Network\Request), Object(Cake\Network\Response))
#9 {main}

I found CakePHP security component blackholing login (data[_Token][key] field not generated), here on StackOverflow, but no other relevant information as to what's causing my problem. In my Appcontroller:

    public function initialize()
    {
        parent::initialize();

        $this->loadComponent('Security');
        $this->loadComponent('RequestHandler');
        $this->loadComponent('Flash');
like image 392
Berry M. Avatar asked May 21 '16 08:05

Berry M.


3 Answers

The error is related to the _TOKEN. When we create a CakePHP form and then based on the input fields the CakePHP generates hidden field named _TOKEN.

For example:

<?= $this->Form->create(false, [
    'id' => "ajaxForm",
    'url' => [
        'controller' => 'TPCalls', 
        'action' => 'add'
    ],
    'class'=> "addUpdateDeleteEventForm"
    ]); 
?>
<?= $this->Form->input('id', ['label' => false]); ?>
<?= $this->Form->input('start', ['label' => false]); ?>
<?= $this->Form->input('end', ['label' => false]); ?>
<?= $this->Form->input('title', ['label' => false]); ?>
<?= $this->Form->hidden('ADD', ['value' => 'true']); ?>
<?= $this->Form->end(); ?>

Now you should see _TOKEN value in the form when inspecting the HTML:

<input type="hidden" name="_Token[fields]" autocomplete="off" value="---HASH---">

If you do not have any visible fields then _Token will be empty. If you need to have invisible fields then simply add a hidden class on the form or the field.

Anyways, back to the main question. The error is caused by the _TOKEN field's absence. In above case, I would serialize my form before making the Ajax call.

    //serializing the form    
    var ajaxdata = $("#ajaxForm").serializeArray();

    //ajax
    $.ajax({
        url:$("#ajaxForm").attr("action"),
        type:"POST",
        beforeSend: function(xhr){
            xhr.setRequestHeader("X-CSRF-Token", $('[name="_csrfToken"]').val());
        },
        data:ajaxdata,
        dataType: "json",
        success:function(response) {
            console.log(response);
        },
        error: function(response) {
            console.error(response.message, response.title);
        }
    });

Please note, in the ajax, I am using URL from the Cakephp form instead of hard coding it in the ajax. This way, it will be using cakephp url helper.

like image 168
Invincible Avatar answered Nov 11 '22 11:11

Invincible


EDIT after @Invincible comment

Be careful when disabling csrf and security components, they provide protection against csrf and things like form-tampering, forcing ssl, http methods etc https://book.cakephp.org/3.0/en/controllers/components/security.html.

This answer shows only how to disable them, in case if you are sure you do not need them for that request.

Original Answer

In case of ajax requests you can can disable Security Component for that specific action (equivalent to making the action as unlocked in cake 2.x)

put this code in your controller's beforeFilter

$actions = [
    'action1',
    'action2'
];

if (in_array($this->request->params['action'], $actions)) {
    // for csrf
    $this->eventManager()->off($this->Csrf);

    // for security component
    $this->Security->config('unlockedActions', $actions);
}

disabling csrf component http://book.cakephp.org/3.0/en/controllers/components/csrf.html#disabling-the-csrf-component-for-specific-actions

disabling security component http://book.cakephp.org/3.0/en/controllers/components/security.html#disabling-security-component-for-specific-actions

like image 36
dav Avatar answered Nov 11 '22 11:11

dav


UPDATE: Also, make sure you didn't forget echo $this->Form->end(); as it adds all the necessary tokens. Original answer below.

UPDATE: You may also run into this issue when submitting a form created separately via new \Cake\View\ViewBuilder()


The correct answer is indeed to invest some time into updating your code to where it's following the security component guidelines. Disabling the component, or unlocking a specific action, is a workaround, and not a solution.

Several not-so-obvious things about _Token.

Prerequisites: I have a placeholder <form> used for building repetitive, almost identical AJAX requests (one field is being constantly updated in a javascript loop and re-submitted; don't ask why).

  • _Token is linked to the form action URL. You can't have your placeholder form point to e.g. javascript:; and have actual requests, signed with it's token, go to some other endpoint

  • _Token is reusable. It is possible to issue multiple requests (i.e. submit the same form over and over again) signed with the same token, it's not a one-time token like I thought

So what I did was put my varying piece of information into a type="text" input and display:none it, instead of using a type="hidden" input, which would fall under the form tampering prevention. Then I serialize()'d the form and put it into the jQuery.ajax() data property.

// update the CSS-hidden type="text" input
document.forms.exampleForm.quantity.value=newValue;
// submit the form
jQuery.ajax({
    url: document.forms.exampleForm.action,
    type: document.forms.exampleForm.method,
    data: jQuery(document.forms.exampleForm).serialize(),
    complete: function(jqXHR) {
        //
    }
});

Surely one could take the easy unlockedActions route but you'll probably be glad if you don't.

like image 20
mehov Avatar answered Nov 11 '22 11:11

mehov