#php #cakephp
#php #cakephp
Вопрос:
Я использую CakePHP 3.4 (не могу обновиться), и для защиты системы от подделки межсайтовых запросов мне нужно установить для CSRF token cookie значение SameSite = Strict. Однако, похоже, эта версия CakePHP не может обработать такую настройку.
Я попытался использовать класс CsrfComponent и загрузить компонент в AppController
$this->loadComponent('Csrf', [
'secure' => true,
'httpOnly' => true,
]);
Как я могу обойти установку этого cookie на SameSite = Strict или другую альтернативу для защиты от подделки межсайтовых запросов?
Комментарии:
1. Зависит от а) вашей версии PHP и б) ваших возможных будущих планов по обновлению? Кроме того, что именно мешает вам обновиться хотя бы до последней версии 3.x?
2. В настоящее время на PHP 7.0 anc не может обновить CakePHP, поскольку проект большой и слишком старый, обновление займет много человеческих часов, адаптируя все, что устарело.
Ответ №1:
В CakePHP 3.9.3 добавлена поддержка samesite с CSRF cookies, однако вам придется переключиться на промежуточное программное обеспечение защиты CSRF.
Если вы не можете выполнить обновление, тогда вам понадобится немного пользовательского кода, а именно пользовательский / расширенный компонент CSRF, который принимает дополнительные параметры для атрибута, и пользовательский / расширенный объект ответа, который создает файлы cookie с этим атрибутом соответственно.
В версиях PHP, более ранних, чем PHP 7.3, вы можете, соответственно, должны вводить SameSite
атрибут, используя взлом пути к cookie, который состоит в добавлении дополнительных атрибутов cookie к пути, просто закрыв путь точкой с запятой. В версиях PHP, начиная с PHP 7.3, вы бы использовали значение, которое тогда поддерживалось samesite
для setcookie()
.
кстати, для сеансовых файлов cookie вы должны соответствующим образом изменить свои session.cookie_path
или session.cookie_samesite
PHP INI параметры, и другие места в CakePHP, которые устанавливают файлы cookie, возможно, также потребуется адаптировать, например, компонент cookie, даже если ваше приложение его не использует, он может использоваться сторонними плагинами.
Пример:
<?php
// in src/Controller/Component/CsrfComponent.php
namespace AppControllerComponent;
use CakeControllerComponentRegistry;
use CakeHttpResponse;
use CakeHttpServerRequest;
class CsrfComponent extends CakeControllerComponentCsrfComponent
{
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 AppHttp;
class Response extends CakeHttpResponse
{
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 CakeHttpResponse;
use CakeHttpServerRequest;
// ...
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 AppHttpResponse();
parent::__construct($request, $response, $name, $eventManager, $components);
}
// ...
}
Наконец, создайте псевдоним Csrf
компонента с помощью пользовательского класса component и задайте свою samesite
конфигурацию:
// in src/Controller/AppController.php
// ...
class AppController extends Controller
{
// ...
public function initialize()
{
parent::initialize();
// ...
$this->loadComponent('Csrf', [
'className' => AppControllerComponentCsrfComponent::class,
'secure' => true,
'httpOnly' => true,
'samesite' => 'Strict',
]);
}
// ...
}
В заключение следует отметить, что в более поздних версиях CakePHP 3.x массивы файлов cookie были заменены объектами cookie, что потребовало бы соответствующих изменений, но поскольку вы не можете обновиться, это должно быть нормально, и как только вы решите обновиться, стреляйте по звездам и обновитесь допоследняя версия.