cakephp: Cake 4.0.5 to 4.0.6 upgrade: Missing CSRF token body Cake\Http\Exception\InvalidCsrfTokenException

This is a (multiple allowed):

  • bug

  • enhancement

  • feature-discussion (RFC)

  • CakePHP Version: 4.0.6

  • Platform and Target: Ubuntu 18 LTS, Apache, PHP 7.2 FPM

What you did

  1. Upgrade to 4.0.5 from 4.0.6
  2. Went to do login in on my website
  3. Encountered Missing CSRF token body Cake\Http\Exception\InvalidCsrfTokenException

What happened

This happens on any form submission. A workaround is clearing cookie, but iis it reasonable to force clearing this data on each release to avoid any potential CSRF errors? I have locked my CakePHP version to 4.0.5 for now.

Stack Trace:

Missing CSRF token body
Cake\Http\Exception\InvalidCsrfTokenException
Toggle Vendor Stack Frames
CORE/src/Http/Middleware/CsrfProtectionMiddleware.php:254
Cake\Http\Middleware\CsrfProtectionMiddleware->_validateToken
CORE/src/Http/Middleware/CsrfProtectionMiddleware.php:133
Cake\Http\Middleware\CsrfProtectionMiddleware->process
CORE/src/Http/Runner.php:73
Cake\Http\Runner->handle
CORE/src/Http/Runner.php:58
Cake\Http\Runner->run
CORE/src/Routing/Middleware/RoutingMiddleware.php:162
Cake\Routing\Middleware\RoutingMiddleware->process
CORE/src/Http/Runner.php:73
Cake\Http\Runner->handle
CORE/src/Routing/Middleware/AssetMiddleware.php:68
Cake\Routing\Middleware\AssetMiddleware->process
CORE/src/Http/Runner.php:73
Cake\Http\Runner->handle
CORE/src/Http/Middleware/DoublePassDecoratorMiddleware.php:74
Cake\Http\Middleware\DoublePassDecoratorMiddleware->Cake\Http\Middleware\{closure}
ROOT/vendor/dereuromark/cakephp-setup/src/Middleware/MaintenanceMiddleware.php:48
Setup\Middleware\MaintenanceMiddleware->__invoke
CORE/src/Http/Middleware/DoublePassDecoratorMiddleware.php:75
Cake\Http\Middleware\DoublePassDecoratorMiddleware->process
CORE/src/Http/Runner.php:73
Cake\Http\Runner->handle
CORE/src/Error/Middleware/ErrorHandlerMiddleware.php:119
Cake\Error\Middleware\ErrorHandlerMiddleware->process
CORE/src/Http/Runner.php:73
Cake\Http\Runner->handle
CORE/src/Http/Middleware/CspMiddleware.php:67
Cake\Http\Middleware\CspMiddleware->process
CORE/src/Http/Runner.php:73
Cake\Http\Runner->handle
CORE/src/Http/Middleware/SecurityHeadersMiddleware.php:257
Cake\Http\Middleware\SecurityHeadersMiddleware->process
CORE/src/Http/Runner.php:73
Cake\Http\Runner->handle
ROOT/vendor/cakephp/debug_kit/src/Middleware/DebugKitMiddleware.php:60
DebugKit\Middleware\DebugKitMiddleware->process
CORE/src/Http/Runner.php:73
Cake\Http\Runner->handle
CORE/src/Http/Runner.php:58
Cake\Http\Runner->run
CORE/src/Http/Server.php:90
Cake\Http\Server->run
ROOT/webroot/index.php:40
/var/www/personal/easyupp/vendor/cakephp/cakephp/src/Http/Middleware/CsrfProtectionMiddleware.php
Toggle Arguments
        if ($this->_compareToken($header, $cookie)) {
            return;
        }
        throw new InvalidCsrfTokenException(__d('cake', 'Missing CSRF token body'));
    }
    /**
     * Ensure that the request token matches the cookie value and that

middleware:

    public function middleware(MiddlewareQueue $middlewareQueue): MiddlewareQueue
    {
        Configure::load('easyupp/frontend', 'default');
        $policies = Configure::read('Frontend.ContentSecurityPolicy');

        $securityHeaders = new SecurityHeadersMiddleware();
        $securityHeaders
            ->setCrossDomainPolicy()
            ->setReferrerPolicy()
            ->setXFrameOptions()
            ->setXssProtection()
            ->noOpen()
            ->noSniff();

        $middlewareQueue
            ->add($securityHeaders)
            ->add(new CspMiddleware($policies))
            // Catch any exceptions in the lower layers,
            // and make an error page/response
            ->add(new ErrorHandlerMiddleware(Configure::read('Error')))
            // https://github.com/dereuromark/cakephp-setup/blob/master/docs/Maintenance/Maintenance.md
            ->add(MaintenanceMiddleware::class)
            // Handle plugin/theme assets like CakePHP normally does.
            ->add(new AssetMiddleware([
                'cacheTime' => Configure::read('Asset.cacheTime'),
            ]))
            ->add(new RoutingMiddleware($this));

        return $middlewareQueue;
    }

The site is also using the FormProtection component, but disabling this had no effect when debugging this.

What you expected to happen

Not to get this error.

About this issue

  • Original URL
  • State: closed
  • Created 4 years ago
  • Comments: 22 (16 by maintainers)

Most upvoted comments

Maybe we can update the bakery post to explain that the token is also generated differently instead of just saying we validate it better.

Please open a new issue.

PR has a failing unit test. I wasn’t exactly sure how to tackle that. Interested in how that will be written now.

After reading through https://github.com/cakephp/cakephp/commit/1cee60b61f07856c93696534da50993a3ef70a22 a simple modification to _validateToken in CsrfProtectionMiddleware.php seems to make this fix backward compatible.

    protected function _validateToken(ServerRequestInterface $request): void
    {
        $cookie = Hash::get($request->getCookieParams(), $this->_config['cookieName']);

        if (!$cookie) {
            throw new InvalidCsrfTokenException(__d('cake', 'Missing CSRF token cookie'));
        }

        $body = $request->getParsedBody();
        if (is_array($body) || $body instanceof ArrayAccess) {
            $post = Hash::get($body, $this->_config['field']);
            if (Security::constantEquals($post, $cookie)) {
                return;
            } else if ($this->_compareToken($post, $cookie)) {
                return;
            }
        }

        $header = $request->getHeaderLine('X-CSRF-Token');
        if (Security::constantEquals($header, $cookie)) {
            return;
        } else if ($this->_compareToken($header, $cookie)) {
            return;
        }

        throw new InvalidCsrfTokenException(__d('cake', 'Missing CSRF token body'));
    }