CakePHP 4 で CSRF 保護を一部または完全に無効化(有効化)する方法

はじめに

CakePHP 4 ではデフォルトで CSRF 保護が有効になっています。しかし API の実装など CSRF 保護を使わない場合もあります。

今日は CakePHP 4 で CSRF 保護を一部もしくは全体で無効化(有効化)する方法をご紹介します。

今回使用したのは CakePHP 4.0.8 です

目次
  1. 下準備
  2. 完全に無効
  3. 一部のアクションで無効 or 有効
  4. 特定スコープのみ有効
  5. 終わりに

1. 下準備

今回は Samples コントローラに add と edit の2つのアクションをつくり、テンプレートは共用にしました。テンプレートには CSRF トークンを含めず、CSRF 保護が有効な場合には送信時にエラーが出るようにしています。

/src/Controller/SamplesController.php
<?php
declare(strict_types=1);

namespace App\Controller;

class SamplesController extends AppController
{
    public function add()
    {
        return $this->render('form');
    }

    public function edit()
    {
        return $this->render('form');
    }
}
/templates/Samples/form.php
<h1><?= $this->request->getParam('action') ?></h1>
<form method="post">
    <button type="submit">submit</button>
</form>

2. 完全に無効

CSRF 保護を全アクションで無効化する方法は src/Application.php の当該箇所をコメントアウト(もしくは削除)するだけです。

/src/Application.php
// ▼ ここをコメントアウト or 削除
// Cross Site Request Forgery (CSRF) Protection Middleware
// https://book.cakephp.org/4/en/controllers/middleware.html#cross-site-request-forgery-csrf-middleware
// ->add(new CsrfProtectionMiddleware([
//     'httponly' => true,
// ]));
;

3. 一部のアクションで無効 or 有効

一部分だけを無効化するには skipCheckCallback() を使って実装できます。

下記コードでは Samples コントローラの edit アクションのみ無効化しています。
(add 画面では有効なので、送信すると InvalidCsrfTokenException のエラーが出ます)

IF 文の条件式を変えれば「特定のアクションだけ有効」(= 特定のアクション以外は無効)にすることもできますね。

/src/Application.php
public function middleware(MiddlewareQueue $middlewareQueue): MiddlewareQueue
{
    $middlewareQueue
      ...
      // ▼ ここをコメントアウト or 削除
      // Cross Site Request Forgery (CSRF) Protection Middleware
      // https://book.cakephp.org/4/en/controllers/middleware.html#cross-site-request-forgery-csrf-middleware
      // ->add(new CsrfProtectionMiddleware([
      //     'httponly' => true,
      // ]));
      ;

    // ▼下記を追加
    $csrf = new CsrfProtectionMiddleware([
        'httponly' => true,
    ]);
    $csrf->skipCheckCallback(function ($request) {
        if (
            $request->getParam('controller') === 'Samples'
            && $request->getParam('action') === 'edit'
        ) {
            return true;
        }
    });
    $middlewareQueue->add($csrf);

    return $middlewareQueue;
}

公式ドキュメントでは Prefix Routing を用いている場合のサンプルコードが紹介されています。

サンプルコードは下記「Cross Site Request Forgery (CSRF) Middleware」ページのやや下部にあります

4. 特定スコープのみ有効

ルーティング設定で、特定スコープの場合だけ CSRF を有効化することができます。

例えば、下記のように設定すれば /my-samples でアクセスしたときだけ CSRF 保護が有効になります。

/src/Application.php
// ▼ ここをコメントアウト or 削除
// Cross Site Request Forgery (CSRF) Protection Middleware
// https://book.cakephp.org/4/en/controllers/middleware.html#cross-site-request-forgery-csrf-middleware
// ->add(new CsrfProtectionMiddleware([
//     'httponly' => true,
// ]));
;
/config/routes.php
<?php
...
use Cake\Http\Middleware\CsrfProtectionMiddleware; // ←追加
use Cake\Routing\Route\DashedRoute;
use Cake\Routing\RouteBuilder;
...
// ▼これを追加
$routes->scope('/my-samples', function (RouteBUilder $builder) {
    // CSRF 保護を有効化
    $builder->registerMiddleware('csrf', new CsrfProtectionMiddleware([
        'httpOnly' => true,
    ]));
    $builder->applyMiddleware('csrf');

    // /my-samples で /samples/edit にアクセス
    $builder->connect('/', ['controller' => 'Samples', 'action' => 'edit']);
});
...

この route.php で設定する方法は、公式ドキュメントの「Cross Site Request Forgery (CSRF) Middleware」に Application.php に書くサンプルがあるのですが、うまく動かすことができず、同じくチュートリアルにある「Finding Articles By Tags」を参考にしました。

5. おわりに

CakePHP では CSRF 保護を柔軟に設定することができます。

これらの機能を使わなくても実装できるかもしれませんが、うまく使うことで開発効率と保守性が高まりますので、用途に応じて使い分けるのがオススメです。