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

ssm49v7z  于 2022-11-11  发布在  PHP
关注(0)|答案(5)|浏览(125)

编辑:

在问了这个问题之后,我得到了一系列新的见解,这些见解告诉我问题是什么,而且它肯定与所描述的服务器迁移没有任何关系。
这两个答案说明了如何在CakePHP 2和3中“修复”这个问题,但请记住,这可能会带来安全风险。CSRF组件是一个重要的安全特性,不应该轻易禁用。

原始问题:

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

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');
eagi6jfj

eagi6jfj1#

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

<?= $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(); ?>

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

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

如果您没有任何可见字段,则_Token将为空。如果您需要具有不可见字段,则只需在表单或字段上添加一个隐藏类。
无论如何,回到主要问题。这个错误是由于_TOKEN字段的缺失引起的。在上面的例子中,我会在进行 AJAX 调用之前序列化我的表单。

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

请注意,在 AJAX 中,我使用的是来自Cakephp表单的URL,而不是在ajax中硬编码。这样,它将使用Cakephp URL助手。

1dkrff03

1dkrff032#

编辑@无敌评论后

禁用csrf和安全组件时要小心,它们提供了针对csrf和表单篡改、强制ssl、http方法等https://book.cakephp.org/3.0/en/controllers/components/security.html的保护。
此答案仅显示如何禁用它们,以防您确定该请求不需要它们。

原始答案

在 AJAX 请求的情况下,您可以禁用该特定操作的安全组件(相当于在cake 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组件 *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

bqucvtff

bqucvtff3#

UPDATE:另外,确保你没有忘记echo $this->Form->end();,因为它添加了所有必要的令牌。原始答案如下。
UPDATE:您在提交表单created separately via new \Cake\View\ViewBuilder()时也可能会遇到此问题

正确的答案确实是花一些时间更新代码,使其符合安全组件的指导原则。禁用组件或解锁特定操作是一种变通办法,而不是解决方案。
关于_Token的几个不那么明显的事情。

  • 先决条件 *:我有一个占位符<form>,用于构建重复的、几乎相同的 AJAX 请求(一个字段在javascript循环中不断更新并重新提交;不要问为什么)。
    *_Token链接到表单操作URL。您不能让占位符表单指向例如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路线,但你可能会很高兴,如果你不这样做。

mw3dktmi

mw3dktmi4#

@Invincible的答案是好的,但是以这种方式应用csrf似乎是一场应用和维护的噩梦,因为我们的应用程序中已经有大约20个 AJAX 。
所以我使用Cakephp 3 - element来帮助抽象一些代码。如果你也想抽象csrf令牌,我将我的代码粘贴在这里供其他人参考。
下面是代码:

元素: AJAX _元素.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 ,请执行以下操作:

一些页面.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将输出安全错误。

gudnpqoy

gudnpqoy5#

我也遭受了同样的事情,但它被解决了

cakephp2.10.2

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

相关问题