phpcsrfslimmiddlewareslim-4

How do I implement CSRF protection with Slim 4 and slim/csrf?


Slim 4 is already here and I am trying to move to Slim 4. Everything is great, but CSRF returns an error when i try to implement it. I tried the simplest setup, but I get this error:

Message: Argument 2 passed to Slim\Csrf\Guard::__invoke() must be an instance of Psr\Http\Message\ResponseInterface, instance of Slim\Routing\RouteRunner given, called in /Volumes/Web/slim/vendor/slim/slim/Slim/MiddlewareDispatcher.php on line 180

File: /Volumes/Web/slim/vendor/slim/csrf/src/Guard.php

Here is my code:

<?php
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
use Slim\Factory\AppFactory;
use Slim\Csrf\Guard;

require __DIR__ . '/../vendor/autoload.php';

/**
 * Instantiate App
 *
 * In order for the factory to work you need to ensure you have installed
 * a supported PSR-7 implementation of your choice e.g.: Slim PSR-7 and a supported
 * ServerRequest creator (included with Slim PSR-7)
 */
$app = AppFactory::create();

$app->add(Guard::class);

// Add Routing Middleware
$app->addRoutingMiddleware();

/*
 * Add Error Handling Middleware
 *
 * @param bool $displayErrorDetails -> Should be set to false in production
 * @param bool $logErrors -> Parameter is passed to the default ErrorHandler
 * @param bool $logErrorDetails -> Display error details in error log
 * which can be replaced by a callable of your choice.

 * Note: This middleware should be added last. It will not handle any exceptions/errors
 * for middleware added after it.
 */
$errorMiddleware = $app->addErrorMiddleware(true, true, true);

// Define app routes
$app->get('/', function (Request $request, Response $response, $args) {
    $response->getBody()->write('Hello');
    return $response;
});

// Run app
$app->run();

Any help is greatly appreciated! Thanks!


Solution

  • The package is not compatible with Slim4. I wrote a wrapper so you can use it.

    `

    <?php
    
    declare(strict_types=1);
    
    namespace App\Application\Middleware;
    
    use Psr\Http\Message\ResponseInterface as Response;
    use Psr\Http\Message\ServerRequestInterface as Request;
    use Psr\Http\Server\MiddlewareInterface as Middleware;
    use Psr\Http\Server\RequestHandlerInterface as RequestHandler;
    use Slim\Csrf\Guard as Guard;
    
    class CsrfMiddleware extends Guard implements Middleware
    {
    
        /**
         * Process middleware
         *
         * @param  ServerRequestInterface  $request  request object
         * @param  RequestHandlerInterface $handler handler object
         *
         * @return ResponseInterface response object
         */
        public function process(Request $request, RequestHandler $handler): Response
        {
            $this->validateStorage();
            // Validate POST, PUT, DELETE, PATCH requests
            if (in_array($request->getMethod(), ['POST', 'PUT', 'DELETE', 'PATCH'])) {
                $body = $request->getParsedBody();
                $body = $body ? (array) $body : [];
                $name = isset($body[$this->prefix . '_name']) ? $body[$this->prefix . '_name'] : false;
                $value = isset($body[$this->prefix . '_value']) ? $body[$this->prefix . '_value'] : false;
                if (!$name || !$value || !$this->validateToken($name, $value)) {
                    // Need to regenerate a new token, as the validateToken removed the current one.
                    $request = $this->generateNewToken($request);
    
                    $failureCallable = $this->getFailureCallable();
                    return $failureCallable($request, $handler);
                }
            }
            // Generate new CSRF token if persistentTokenMode is false, or if a valid keyPair has not yet been stored
            if (!$this->persistentTokenMode || !$this->loadLastKeyPair()) {
                $request = $this->generateNewToken($request);
            } elseif ($this->persistentTokenMode) {
                $pair = $this->loadLastKeyPair() ? $this->keyPair : $this->generateToken();
                $request = $this->attachRequestAttributes($request, $pair);
            }
            // Enforce the storage limit
            $this->enforceStorageLimit();
    
            return $handler->handle($request);
        }
    
        /**
         * Getter for failureCallable
         *
         * @return callable|\Closure
         */
        public function getFailureCallable()
        {
            if (is_null($this->failureCallable)) {
                $this->failureCallable = function (Request $request, RequestHandler $handler): Response {
                    $response = $handler->handle($request);
                    $stream = $response->getBody();
                    $stream->write('CSRF fail');
                    return $response->withStatus(400);
                };
            }
            return $this->failureCallable;
        }
    }
    

    `