※環境:CakePHP 4.0.3
※現象:jQueryのajaxでPOSTを投げるとき、403(Forbidden)が返されました。
※原因:
「config/routes.php」に全体のCSRFチェックの処理がデフォルトで入っているため、フォーム以外からのPOSTはエラーになります。
※対策:
・「config/routes.php」の全体のCSRFチェックをコメントアウトします。
・「src/Application.php」で個別にホワイトリスト(CSRFのチェックを行わないコントローラとアクション)を設定します。
↓↓(例)
(1)「config/routes.php」の修正:4行コメントアウト
$routes->scope('/', function (RouteBuilder $builder) {
// $builder->registerMiddleware('csrf', new CsrfProtectionMiddleware([ // ← この行をコメントアウト
// 'httpOnly' => true, // ← この行をコメントアウト
// ])); // ← この行をコメントアウト
// $builder->applyMiddleware('csrf'); // ← この行をコメントアウト
// ・・・以下省略・・・
});
(2)「src/Application.php」の修正:3ヶ所追加
use Cake\Http\Middleware\CsrfProtectionMiddleware; // 名前空間の記述を追加
// ・・・中略・・・
public function middleware(MiddlewareQueue $middlewareQueue): MiddlewareQueue
{
// ↓↓ここから追加
$csrf = new CsrfProtectionMiddleware(['httpOnly' => true]);
$csrf->whitelistCallback(function ($request) {
$controller = $request->getParam('controller');
$action = $request->getParam('action');
if (is_null($controller) || is_null($action)) {
return false;
}
// ↓↓ホワイトリストの指定
$aryOkControllersAndActions = [
['controller' => 'Articles', 'action' => 'add'],
['controller' => 'Articles', 'action' => 'edit'],
['controller' => 'Articles', 'action' => 'delete'],
['controller' => 'Users', 'action' => 'add'],
];
// ↑↑ホワイトリストの指定
foreach($aryOkControllersAndActions as $aryOkControllerAndAction) {
if (strcmp($controller, $aryOkControllerAndAction['controller']) == 0 && strcmp($action, $aryOkControllerAndAction['action']) == 0) {
return true;
}
}
return false;
});
// ↑↑ここまで追加
$middlewareQueue
// Catch any exceptions in the lower layers,
// and make an error page/response
->add(new ErrorHandlerMiddleware(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); // ← ここに「->add($csrf)」を追加(セミコロン「;」の記述に要注意)
return $middlewareQueue;
}
↑上記の例では、「Articles/add」「Articles/edit」「Articles/delete」「Users/add」に対してだけ、CSRFチェックを行いません。