【问题标题】:How to set CSRF cookie to samesite on CakePHP 3.4?如何在 CakePHP 3.4 上将 CSRF cookie 设置为相同站点?
【发布时间】:2021-04-23 23:19:40
【问题描述】:

我使用的是 CakePHP 3.4(无法升级),为了保护系统免受 跨站点请求伪造,我需要将 CSRF 令牌 cookie 设置为 SameSite = Strict。但是,这个版本的 CakePHP 似乎无法处理这样的设置。

我尝试过使用 CsrfComponent 类并在 AppController 中加载组件

$this->loadComponent('Csrf', [
            'secure' => true,
            'httpOnly' => true,
        ]);

如何将此 cookie 设置为 SameSite = Strict 或其他替代方案以防止跨站点请求伪造?

【问题讨论】:

  • 取决于 a) 您的 PHP 版本,以及 b) 您未来可能的升级计划?另外,究竟是什么阻止了您至少升级到最新的 3.x?
  • 目前 PHP 7.0 anc 无法升级 CakePHP,因为项目太大太旧,更新将花费大量人力时间来适应所有已弃用的内容。

标签: php cakephp


【解决方案1】:

在 CakePHP 3.9.3 中添加了对带有 CSRF cookie 的同一站点的支持,但您必须切换到 CSRF 保护中间件。

如果您无法升级,那么您将需要一些自定义代码,即一个自定义/扩展 CSRF 组件,该组件接受属性的更多选项,以及一个自定义/扩展响应对象,该对象相应地创建具有该属性的 cookie。

在 PHP 7.3 之前的 PHP 版本中,您可以分别必须通过利用 cookie 路径 hack 注入 SameSite 属性,其中包括将更多 cookie 属性附加到路径,只需关闭带有分号的路径。在 PHP 7.3 以上的 PHP 版本中,您可以使用当时受支持的 samesite 来代替 setcookie()

顺便说一句,对于会话 cookie,您需要相应地修改您的 session.cookie_pathsession.cookie_samesite PHP INI 选项,并且 CakePHP 中设置 cookie 的其他位置也可能需要调整,例如 cookie component,即使您的应用不使用它,它可能被 3rd 方插件使用。

例子:

<?php
// in src/Controller/Component/CsrfComponent.php
namespace App\Controller\Component;

use Cake\Controller\ComponentRegistry;
use Cake\Http\Response;
use Cake\Http\ServerRequest;

class CsrfComponent extends \Cake\Controller\Component\CsrfComponent
{
    public function __construct(ComponentRegistry $registry, array $config = [])
    {
        // Use Lax by default
        $config += [
            'samsite' => 'Lax',
        ];
    
        parent::__construct($registry, $config);
    }
    
    protected function _setCookie(ServerRequest $request, Response $response)
    {
        parent::_setCookie($request, $response);

        // Add samesite option to the cookie that has been created by the parent
        $cookie = $response->cookie($this->getConfig('cookieName'));
        $cookie['samesite'] = $this->getConfig('samesite');

        $response->cookie($cookie);
    }
}

https://github.com/cakephp/cakephp/blob/3.4.14/src/Controller/Component/CsrfComponent.php#L125

// in src/Http/Response.php
namespace App\Http;

class Response extends \Cake\Http\Response
{
    protected function _setCookies()
    {
        foreach ($this->_cookies as $name => $c) {
            if (version_compare(PHP_VERSION, '7.3.0', '<')) {
                // Use regular syntax (with possible path hack) in case
                // no samesite has been set, or the PHP version doesn't
                // support the samesite option.
                
                if (isset($c['samesite'])) {
                    $c['path'] .= '; SameSite=' . $c['samesite'];
                }
                
                setcookie(
                    $name,
                    $c['value'],
                    $c['expire'],
                    $c['path'],
                    $c['domain'],
                    $c['secure'],
                    $c['httpOnly']
                );
            } else {
                setcookie($name, $c['value'], [
                    'expires' => $c['expire'],
                    'path' => $c['path'],
                    'domain' => $c['domain'],
                    'secure' => $c['secure'],
                    'httponly' => $c['httpOnly'],
                    'samesite' => $c['samesite'],
                ]);
            }
        }
    }
}

https://github.com/cakephp/cakephp/blob/3.4.14/src/Http/Response.php#L540

AppController 的构造函数中注入自定义响应对象:

// in src/Controller/AppController.php

use Cake\Http\Response;
use Cake\Http\ServerRequest;

// ...

class AppController extends Controller
{
    // ...

    public function __construct(
        ServerRequest $request = null,
        Response $response = null,
        $name = null,
        $eventManager = null,
        $components = null
    ) {
        if ($response !== null) {
            throw new \InvalidArgumentException(
                'This should not happen, we want to use a custom response class.'
            );
        }
        $response = new \App\Http\Response();
    
        parent::__construct($request, $response, $name, $eventManager, $components);
    }

    // ...
}

最后alias the Csrf component 使用自定义组件类并设置您的samesite 配置:

// in src/Controller/AppController.php

// ...

class AppController extends Controller
{
    // ...
    
    public function initialize()
    {
        parent::initialize();

        // ...
        $this->loadComponent('Csrf', [
            'className' => \App\Controller\Component\CsrfComponent::class,
            'secure' => true,
            'httpOnly' => true,
            'samesite' => 'Strict',
        ]);
    }

    // ...
}

最后应该注意的是,在后来的 CakePHP 3.x 版本中,cookie 数组已被 cookie 对象替换,因此需要相应地进行更改,但由于您无法升级,这应该没问题,并且一旦您决定升级,请为之争星并升级到最新版本。

【讨论】:

    猜你喜欢
    • 2021-03-09
    • 1970-01-01
    • 2016-12-21
    • 2021-06-10
    • 2011-08-17
    • 1970-01-01
    • 2020-02-11
    • 2020-02-07
    相关资源
    最近更新 更多