I'm trying to disable the CSRF check for a single controller (API), but I'm unable to find how I'm able to achieve this.
The pre 3.5.0 CSRF Component had the ability to be disabled on certain request using:
$this->eventManager()->off($this->Csrf);
There are two ways to do that.
Depending on what routes you create, you can apply the middleware to specific scopes only, for example:
// config/routes.php
use Cake\Http\Middleware\CsrfProtectionMiddleware;
Router::scope('/', function ($routes) {
$routes->registerMiddleware('csrf', new CsrfProtectionMiddleware([
'httpOnly' => true
]));
$routes->scope('/api', function ($routes) {
// ...
});
$routes->scope('/blog', function ($routes) {
$routes->applyMiddleware('csrf');
// ...
});
$routes->scope('/cms', function ($routes) {
$routes->applyMiddleware('csrf');
// ...
});
});
This would apply the CSRF middleware only to the routes connected in the blog
and cms
scopes.
It's also possible to narrow things down further to route level, and apply the middleware on specific routes:
$routes
->connect('/blog/:action', ['controller' => 'Blogs'])
->setMiddleware(['csrf']);
This would apply the CSRF middleware to only the /blog/*
routes.
Another way would be to manually the apply the middleware when applicable. As of CakePHP 3.8, the middleware has a whitelisting method named whitelistCallback
, inside of it you can check the request object and return a boolean that defines whether the checks are applied:
// src/Application.php
// ...
use Cake\Http\Middleware\CsrfProtectionMiddleware;
use Psr\Http\Message\ServerRequestInterface;
class Application extends BaseApplication
{
// ...
public function middleware($middleware)
{
$csrf = new CsrfProtectionMiddleware([
'httpOnly' => true
]);
$csrf->whitelistCallback(function (ServerRequestInterface $request) {
$params = $request->getAttribute('params');
return $params['controller'] !== 'Api';
});
$middleware
// ...
->add(new RoutingMiddleware())
->add($csrf);
return $middleware;
}
}
In earlier CakePHP versions you'll have to create a custom middleware handler so that you can access the current request object, from which you can extract the controller
parameter, and then you have to invoke the CSRF middleware inside your handler, something along the lines of this:
// src/Application.php
// ...
use Cake\Http\Middleware\CsrfProtectionMiddleware;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
class Application extends BaseApplication
{
// ...
public function middleware($middleware)
{
$middleware
// ...
->add(new RoutingMiddleware())
->add(function (
ServerRequestInterface $request,
ResponseInterface $response,
callable $next
) {
$params = $request->getAttribute('params');
if ($params['controller'] !== 'Api') {
$csrf = new CsrfProtectionMiddleware([
'httpOnly' => true
]);
// This will invoke the CSRF middleware's `__invoke()` handler,
// just like it would when being registered via `add()`.
return $csrf($request, $response, $next);
}
return $next($request, $response);
});
return $middleware;
}
}
Note that in both cases you must put the middleware after the routing middleware, as that is where the controller information is being set.
If applicable, you could also test against the request URL instead of the routing paramteres, for example something like this:
if (mb_strpos($request->getUri()->getPath(), '/api/') === false) {
// apply CSRF checks
}
When doing so, the middleware wouldn't be restricted to be placed after the routing middleware, you could theoretically place it at whichever position you want.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With