在 CakePHP 3 的特定操作上禁用 CSRF

Disabling CSRF on a specific action CakePHP 3

因此,我有一个使用 DataTables 自动生成的 table。我的 CakePHP 中的一个动作获取 table 的数据,并将其格式化为 JSON 以供数据 table 使用,这是格式化的 JSON:

<?php
$data = array();
if (!empty($results)) {
    foreach ($results as $result) {
        $data[] = [
          'name' => $result->name,
          'cad' => $this->Number->currency($result->CAD, 'USD'),
          'usd' => $this->Number->currency($result->USD, 'USD'),
          'edit' => '<a href="' .
            $this->Url->build(['controller' => 'Portfolios', 'action' => 'edit', $result->id]) .
    '"><i class="fa fa-pencil"></i></a>',
          'delete' => '<input type="checkbox" class="delete" value="' . $result->id . '">'
        ];
    }
}

echo json_encode(compact('data'));

如您所见,我在那里有一个 'delete' 选项,它输出一个复选框,该复选框的值是相应元素的 ID。选中该复选框后,将显示一个删除按钮,该按钮发送此 ajax 请求:

$('a#delete').on('click', function(e) {
    e.preventDefault();
    var checkedValues = [];
    $('input.delete:checked').each(function() {
        checkedValues.push($(this).val());
    });
    $.ajax({
        url: $(this).attr('href'),
        type: 'POST',
        data: checkedValues
    });
})

这个 ajax post 转到我的控制器操作 delete()。我遇到的问题是我收到一个错误 "Invalid Csrf Token"。我知道为什么会这样,我正在提交一个启用了 Csrf 保护的表单,其中没有添加 Csrf 令牌。

我不知道如何针对这种情况手动创建 Csrf 令牌(输入值是在页面加载后生成的)。我也不知道如何禁用 Csrf 保护。我读了 this,但是代码放在了 beforeFilter 函数中,据我了解,这意味着每个动作都是 运行,而不仅仅是这个,这不是我想要的。另外,老实说,我更喜欢不禁用安全功能的解决方案。

是否有针对此特定操作禁用 Csrf 的方法,或者是否有更好的方法来执行此操作?

在此处阅读有关 CSRF 组件的所有信息

http://book.cakephp.org/3.0/en/controllers/components/csrf.html

您可以在此处禁用特定操作:

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

 public function beforeFilter(Event $event) {
     if (in_array($this->request->action, ['actions_you want to disable'])) {
         $this->eventManager()->off($this->Csrf);
     }
 }

以上答案不适用于 Cakephp 3.6 或更高版本。

Cakephp在src/Application.php中添加CsrfProtectionMiddleware对象。 如果您必须为特定控制器或操作删除 CSRF 保护,那么您可以使用以下解决方法:

public function middleware($middlewareQueue)
{
    $middlewareQueue = $middlewareQueue
        // Catch any exceptions in the lower layers,
        // and make an error page/response
        ->add(ErrorHandlerMiddleware::class)

        // Handle plugin/theme assets like CakePHP normally does.
        ->add(AssetMiddleware::class)

        // Add routing middleware.
        // Routes collection cache enabled by default, to disable route caching
        // pass null as cacheConfig, example: `new RoutingMiddleware($this)`
        // you might want to disable this cache in case your routing is extremely simple
        ->add(new RoutingMiddleware($this, '_cake_routes_'));
        /*
        // Add csrf middleware.
        $middlewareQueue->add(new CsrfProtectionMiddleware([
            'httpOnly' => true
        ]));
        */
    //CSRF has been removed for AbcQutes controller
    if(strpos($_SERVER['REQUEST_URI'], 'abc-quotes')===false){
        $middlewareQueue->add(new CsrfProtectionMiddleware([
            'httpOnly' => true
        ]));
    }
    return $middlewareQueue;
}

所以我需要修复 cakephp 3.7 并且使用 $_SERVER['REQUEST_URI'] 真的不是去这里的方法。因此,这是阅读一些文档后应该如何做的。

在src/Application.php中添加这个函数

public function routes($routes)
{
    $options = ['httpOnly' => true];
    $routes->registerMiddleware('csrf', new CsrfProtectionMiddleware($options));
    parent::routes($routes);
}

注释掉现有的 CsrfProtectionMiddleware

public function middleware($middlewareQueue)
{ 
  ...
  //            $middlewareQueue->add(new CsrfProtectionMiddleware([
  //                'httpOnly' => true
  //            ]));
}

打开你的 config/routes.php 添加 $routes->applyMiddleware('csrf');你想要的地方

Router::prefix('api', function ($routes)
{
  $routes->connect('/', ['controller' => 'Pages', 'action' => 'index']);
  $routes->fallbacks(DashedRoute::class);
});

Router::scope('/', function (RouteBuilder $routes)
{
  $routes->applyMiddleware('csrf');
  $routes->connect('/', ['controller' => 'Pages', 'action' => 'dashboard']);
  $routes->fallbacks(DashedRoute::class);
});

请注意,我的 api 用户现在没有 csrf 保护,而基本调用有它。 如果你有更多的前缀,不要忘记在那里添加函数。

在 Application.php 这对我有用....

    $csrf = new CsrfProtectionMiddleware();
    
    // Token check will be skipped when callback returns `true`.
    $csrf->whitelistCallback(function ($request) {
    // Skip token check for API URLs.
      if ($request->getParam('controller') === 'Api') {
          return true;
      } 

    });