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);
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.
prefix
option in URL arrays. If something doesn't work for you, please create a new question where you can properly elaborate on the problem, show your code, etc. –
Kayseri I think in Cake 3.6, you should remove CsrfProtectionMiddleware from middleware:
queue: src/Application.php
public function middleware($middlewareQueue)
{
$middlewareQueue
// Catch any exceptions in the lower layers,
// and make an error page/response
->add(ErrorHandlerMiddleware::class)
// Handle plugin/theme assets like CakePHP normally does.
->add(new AssetMiddleware([
'cacheTime' => Configure::read('Asset.cacheTime')
]))
// Add routing middleware.
// Routes collection cache enabled by default, to disable route caching
// pass null as cacheConfig, example: `new RoutingMiddleware($this)`
// you might want to disable this cache in case your routing is extremely simple
->add(new RoutingMiddleware($this, '_cake_routes_'));
// Add csrf middleware.
// ->add(new CsrfProtectionMiddleware([
// 'httpOnly' => true
// ]));
return $middlewareQueue;
}
With Cakephp 3.8 I commented all methods implementations into class \vendor\cakephp\cakephp\src\Http\Middleware\CsrfProtectionMiddleware.php, so Csrf middleware is disabled :-)
A more recent solution for 3.8, 3.9
First remove the middleware from route.php.
Router::scope('/', function (RouteBuilder $routes) {
// Register scoped middleware for in scopes.
$routes->registerMiddleware('csrf', new CsrfProtectionMiddleware([
'httpOnly' => true
]));
/**
* Apply a middleware to the current route scope.
* Requires middleware to be registered via `Application::routes()` with `registerMiddleware()`
*/
$routes->applyMiddleware('csrf');
//...
}
Comment registerMiddleware and applyMiddleware('csrf') :
Router::scope('/', function (RouteBuilder $routes) {
// // Register scoped middleware for in scopes.
// $routes->registerMiddleware('csrf', new CsrfProtectionMiddleware([
// 'httpOnly' => true
// ]));
//
// /**
// * Apply a middleware to the current route scope.
// * Requires middleware to be registered via `Application::routes()` with `registerMiddleware()`
// */
// $routes->applyMiddleware('csrf');
//...
}
Now, edit middleware() function from /Application.php :
/**
* Setup the middleware queue your application will use.
*
* @param \Cake\Http\MiddlewareQueue $middlewareQueue The middleware queue to setup.
* @return \Cake\Http\MiddlewareQueue The updated middleware queue.
*/
public function middleware($middlewareQueue)
{
// loading csrf Middleware ---- Add this code Block
$csrf = new CsrfProtectionMiddleware([
'httpOnly' => true
]);
$csrf->whitelistCallback(function (ServerRequest $request) {
// skip controllers
$skipedControllers = ['MyControllerToSkip', 'MyControllerToSkip2']; // EDIT THIS
if(in_array($request->getParam('controller'),$skipedControllers)) {
return true;
}
// skip debugkit
if($request->getParam('plugin') == 'DebugKit') {
return true;
}
return $request;
});
// end codeblock to add, you have to add a other code line below ->add(new RoutingMiddleware($this)) -------
$middlewareQueue
// Catch any exceptions in the lower layers,
// and make an error page/response
->add(new ErrorHandlerMiddleware(null, Configure::read('Error')))
// Handle plugin/theme assets like CakePHP normally does.
->add(new AssetMiddleware([
'cacheTime' => Configure::read('Asset.cacheTime')
]))
// Add routing middleware.
// If you have a large number of routes connected, turning on routes
// caching in production could improve performance. For that when
// creating the middleware instance specify the cache config name by
// using it's second constructor argument:
// `new RoutingMiddleware($this, '_cake_routes_')`
->add(new RoutingMiddleware($this))
->add($csrf); // <---- Don't forget to add this !
return $middlewareQueue;
}
Don't forget the final ->add($csrf); after ->add(new RoutingMiddleware($this)) .
© 2022 - 2024 — McMap. All rights reserved.
cms, blog and api
are prefies? I am bit confused with Scope and Prefix. Also I am unable to use first option using router for normalcontroller/action
URL. Can you help me a bit? – Erudite