服务器迁移后,CakePHP3中的请求数据中找不到“_Token”

时间:2016-05-21 08:42:41

标签: php cakephp

编辑:

在提出这个问题之后获得的一系列新见解告诉了我这个问题是什么,它肯定与描述的服务器迁移没有任何关系。

两个给出的答案显示了如何为CakePHP 2和3“修复”这个问题,但请记住这可能会带来安全风险。 CSRF组件是一项重要的安全功能,不应轻易禁用。

原始问题:

我将CakePHP 3项目从笔记本电脑上的XAMPP迁移到服务器上的XAMPP。自从我激活安全组件后,蛋糕就抛出了一个错误。在这里,它直接来自错误日志:

    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}

我在StackOverflow上找到CakePHP security component blackholing login (data[_Token][key] field not generated),但没有关于导致我的问题的其他相关信息。在我的Appcontroller中:

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

        $this->loadComponent('Security');
        $this->loadComponent('RequestHandler');
        $this->loadComponent('Flash');

6 个答案:

答案 0 :(得分:4)

@Invincible评论后

编辑

在禁用csrf和安全组件时要小心,它们可以防止csrf和形式篡改,强制ssl,http方法等https://book.cakephp.org/3.0/en/controllers/components/security.html

此答案仅显示如何禁用它们,以防您确定不需要它们。

原始答案

如果是ajax请求,您可以为该特定操作禁用安全组件(相当于在蛋糕2.x中将操作解锁)

将此代码放入您的控制器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);
}

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

停用安全组件 http://book.cakephp.org/3.0/en/controllers/components/security.html#disabling-security-component-for-specific-actions

答案 1 :(得分:3)

该错误与_TOKEN有关。当我们创建CakePHP表单,然后基于输入字段时,CakePHP会生成名为_TOKEN的隐藏字段。

例如:

<?= $this->Form->create(false, [
    'id' => "ajaxForm",
    'url' => [
        'controller' => 'TPFeedbackCalls', 
        '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(); ?>

现在,在检查HTML时,您应该在表单中看到_TOKEN值:

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

如果您没有任何可见字段,则_Token将为空。

无论如何,回到主要问题。该错误是由于缺少_TOKEN字段引起的。就我而言,我必须序列化表单并进行Ajax调用。

var ajaxdata = $("#ajaxForm").serializeArray();
$.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);
    }
});

答案 2 :(得分:2)

正确的答案的确是花一些时间将代码更新到遵循安全组件准则的位置。禁用组件或解锁特定操作是一种解决方法,而不是解决方案。

该答案已经发布在这里,但是我想对_Token添加一些不太明显的内容。

先决条件:我有一个占位符<form>,用于建立重复的,几乎相同的AJAX请求(一个字段正在javascript循环中不断更新并重新提交;请不要问为什么)。

  • _Token已链接到表单操作网址。您不能将占位符形式指向例如javascript:;并有使用令牌签名的实际请求,请转到其他端点

  • _Token可重复使用。可以发出使用相同令牌签名的多个请求(即一次又一次地提交相同的表单),这不是我认为的一次性令牌

因此,我所做的就是将各种信息放入type="text"输入并display:none中,而不是使用type="hidden"输入,这属于防止篡改的形式。然后,我serialize()'d将该表单放入jQuery.ajax()数据属性中。

// 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) {
        //
    }
});

当然可以选择简单的unlockedActions路线,但如果不这样做,您可能会很高兴。

答案 3 :(得分:0)

@Invincible的回答总体来说是好的,但是以这种方式应用csrf似乎是应用和维护的噩梦,因为我们的应用程序中已经有大约20个Ajax。

因此,我使用Cakephp 3 - element来帮助抽象一些代码。如果您还想抽象化csrf令牌,我将在此处粘贴我的代码以供其他参考。

代码如下:

元素:csrf_ajax_element.ctp

<?= 
    $this->Form->create(false, [ 
        "id" => $name . "Form",
        "url" => $url,
    ]); 
?>

<? if(isset($params)): ?>
    <? foreach($params as $param) : ?>
    <?= $this->Form->input($param, ['label' => false, 'style' => 'display:none;']); ?>
    <? endforeach; ?>
<? endif; ?>
<?= $this->Form->end(); ?>

<script type="text/javascript">
    var csrfName = '<?=$name?>';
    var url = '<?= $this->Url->build($url) ?>';
    var csrf = { };
    $.each($('#'+csrfName+'Form').serializeArray(), function() {csrf[this.name] = this.value;});

    $("#"+csrfName).data('csrf', csrf);
    $("#"+csrfName).data('url', url);
</script>

要在页面上添加ajax,请执行以下操作:

some_page.ctp

<!-- At the top -->

<input id="myAjaxCsrfToken" type="hidden" data-csrf="" data-url="" />

<?= $this->element('csrf_ajax_element', 
    [
        "name" => "myAjaxCsrfToken", 
        "params" => ['year'],
        "url" => ["controller" => "Api", "action" => "myAjax", "_method" => "POST" ]
    ]) 
?>

<!-- When you need to use the ajax -->
<script type="text/javascript">
$.ajax({
    url: $("#myAjaxCsrfToken").data('url'),
    type: 'POST',
    data: $.extend(
        $("#myAjaxCsrfToken").data('csrf'), 
        { year: 2019 }
    ),
    complete: function() {
        // things
    }
});
</script>

注意:在上面,year是一个自定义参数,需要与token参数一起传递给ajax,如果不这样做,cakephp将输出安全错误。

答案 4 :(得分:-1)

我也遇到了同样的事情,但它已经解决了

cakephp2.10.2

$this->Security->unlockedActions = array('action1', 'action2');

答案 5 :(得分:-3)

for Cakephp 3.4 -3.5

if (in_array($this->request->getAttribute('params')['action']), $actions)) 
{
    $this->getEventManager()->off($this->Csrf);
    $this->Security->setConfig('unlockedActions', $actions);
}