从Slim CSRF中间件中排除路由

时间:2018-04-02 09:09:22

标签: php csrf slim middleware slim-3

我正在使用Twig前端开发基于Slim 3的应用程序,我也在制作REST API。

我已经为整个应用程序实现了slimphp \ Slim-Csrf,但我现在想要从每个" API"中排除这个CSRF检查。路由。

我试图实施"选项2"这篇文章: Slim3 exclude route from CSRF Middleware

以下是代码:

文件App \ Middleware \ CsrfMiddleware.php:     

namespace App\Middleware;

class CsrfMiddleware extends \Slim\Csrf\Guard {

    public function processRequest($request, $response, $next) {
        // Check if this route is in the "Whitelist"
        $route = $request->getAttribute('route');

        if ($route->getName() == 'token') {
            var_dump('! problem HERE, this middleware is executed after the CsrfMiddleware !');
            // supposed to SKIP \Slim\Csrf\Guard
            return $next($request, $response);
        } else {
            // supposed to execute \Slim\Csrf\Guard
            return $this($request, $response, $next);
        }
    }
}

文件app \ app.php:     

$app = new \Slim\App([
    'settings' => [
        'determineRouteBeforeAppMiddleware' => true
    ]
]);

require('container.php');
require('routes.php');

$app->add($container->csrf);
$app->add('csrf:processRequest');

文件app \ container.php:

$container['csrf'] = function ($container) {
    return new App\Middleware\CsrfMiddleware;
};

文件app \ routes.php:

<?php
$app->get('/', \App\PagesControllers\LieuController::class.':home')->setName('home');

$app->post('/api/token', \App\ApiControllers\AuthController::class.'postToken')->setName('token');

当我在http://localhost/slim3/public/api/token上发出POST请求时,我已经:

CSRF检查失败!字符串(70)&#34;!问题在这里,这个中间件是在CsrfMiddleware之后执行的!&#34;

就像我的CsrfMiddleware在\ Slim \ Csrf \ Guard ...

之后执行一样

有人有想法吗?

谢谢。

2 个答案:

答案 0 :(得分:1)

在Slim 3中,中间件是LIFO(后进先出)。 以相反的方向添加中间件:

之前

$app->add($container->csrf);
$app->add('csrf:processRequest');

$app->add('csrf:processRequest');
$app->add($container->csrf);

注意public目录不应该是网址的一部分

不正确:http://localhost/slim3/public/api/token

正确:http://localhost/slim3/api/token

要跳过中间件中的处理,只需返回$ response对象。

// supposed to SKIP \Slim\Csrf\Guard
return $response;

答案 1 :(得分:0)

这是我通过Slim 3实现此目标的方法。

1)创建一个扩展\ Slim \ Csrf \ Guard的类,如下所示。

CsrfGuardOverride类是启用或禁用路径的CSRF检查的关键。如果当前路径已列入白名单,则__invoke()方法将跳过核心CSRF检查,并通过执行下一个中间件层继续进行。

如果当前路径不在白名单中(即应检查CSRF),则__invoke方法将按照其父\ Slim \ Csrf \ Guard :: __ invoke()来以常规方式处理CSRF。

<?php

namespace App\Middleware;

use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use \Slim\Csrf\Guard;

class CsrfGuardOverride extends Guard {

    /**
     * Invoke middleware
     *
     * @param  ServerRequestInterface  $request  PSR7 request object
     * @param  ResponseInterface $response PSR7 response object
     * @param  callable          $next     Next middleware callable
     *
     * @return ResponseInterface PSR7 response object
     */
    public function __invoke(ServerRequestInterface $request, ResponseInterface $response, callable $next)
    {
        // Set the name of the route we want whitelisted with a name
        // prefix of 'whitelist'.  check for that here, and add
        // any path to the white list
        $route = $request->getAttribute('route');
        $routeName = $route->getName();
        $whitelisted = strpos($routeName, 'whitelist');

        // if url is whitelisted from being CSRF checked, then bypass checking by skipping directly to next middleware
        if ($whitelisted !== FALSE) {
            return $next($request, $response);
        }

        return parent::__invoke($request, $response, $next);
    }

}

2)注册CsrfGuardOverride类。确保设置settings.determineRouteBeforeAppMiddleware => true,因为这会强制Slim在执行任何中间件之前评估路由。

    // Method on App Class
    protected function configureContainer(ContainerBuilder $builder)
    {
        parent::configureContainer($builder);

        $definitions = [
            'settings.displayErrorDetails' => true,
            'settings.determineRouteBeforeAppMiddleware' => true,

            // Cross-Site Request Forgery protection
            \App\Middleware\CsrfGuardOverride::class => function (ContainerInterface $container) {
                $guard = new \App\Middleware\CsrfGuardOverride;
                $guard->setPersistentTokenMode(true);   // allow same CSRF token for multiple ajax calls per session
                return $guard;
            },
            'csrf' => DI\get(\App\Middleware\CsrfGuardOverride::class),

            // add others here...
        ];

        $builder->addDefinitions($definitions);
    }

3)通过绕过CSRF检查来添加所需的路径,并为其添加名称以“ whitelist”为前缀:

$app->post('/events/purchase', ['\App\Controllers\PurchaseController', 'purchaseCallback'])->setName('whitelist.events.purchase');