もがき系プログラマの日常

もがき系エンジニアの勉強したこと、日常のこと、気になっている技術、備忘録などを紹介するブログです。

cakephp3でcsrfの制御を行う

はじめに

こんばんは。

今回もまたcakephp3の話です。

外部とのやり取りを行う開発をしており、 外部からのコールバック時に csrfチェックをスルーさせるようなロジックが必要になりました。

その際の対応を備忘録として残しておきます。

book.cakephp.org

本題

現在3.6系のcakephp3を使用中で、その際は、 Application.phpの以下の場所で CsrfProtectionMiddleware を使用しています。

github.com

cakephp4以降になると、CsrfProtectionMiddlewareに whitelistCallback()というメソッドが実装され、ホワイトリスト形式でcsrf保護を通すようなロジックを書ける場所を提供してくれています。

    /**
     * Set callback for allowing to skip token check for particular request.
     *
     * The callback will receive request instance as argument and must return
     * `true` if you want to skip token check for the current request.
     *
     * @param callable $callback A callable.
     * @return $this
     */
    public function whitelistCallback(callable $callback)
    {
        $this->whitelistCallback = $callback;

        return $this;
    }

ちなみに、4.1以降になると、 whitelistCallback() は非推奨になり、代わりに、 skipCheckCallback() を使うように勧められます。

    /**
     * Set callback for allowing to skip token check for particular request.
     *
     * The callback will receive request instance as argument and must return
     * `true` if you want to skip token check for the current request.
     *
     * @param callable $callback A callable.
     * @return $this
     */
    public function skipCheckCallback(callable $callback)
    {
        $this->skipCheckCallback = $callback;

        return $this;
    }

ただし、cakephp3にはこれはありません。 なので、それっぽいのを作る感じになります。

今回作ったのは、 CsrfProtectionMiddleware をextendsした、 AppCsrfProtectionMiddleware です。

<?php
namespace App\Middleware;

use Cake\Http\Middleware\CsrfProtectionMiddleware;
use Cake\Http\Response;
use Cake\Http\ServerRequest;

class AppCsrfProtectionMiddleware extends CsrfProtectionMiddleware
{
    private const WHITE_LIST_REQUESTS = [
        'Example'   => '*',
        'Example2' => ['index', 'callback'],
    ];

    public function __invoke(ServerRequest $request, Response $response, $next)
    {
        $requestParams = $request->getAttributes()['params'];
        foreach (self::WHITE_LIST_REQUESTS as $controller => $actions) {
            if ($requestParams['controller'] !== $controller) {
                continue;
            }

            if ($actions === '*') {
                return $next($request, $response);
            }

            foreach ($actions as $action) {
                if ($requestParams['action'] === $action) {
                    return $next($request, $response);
                }
            }
        }

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

WHITE_LIST_REQUESTSController => Action という感じで定義していくことで、該当のActionに対しては csrfの保護を外すことが出来ます。

終わりに

ということで、こんなのを実装しましたが、cakphp4系になればこんな独自実装は必要なくなるので、早めにあげられたらいいなと思う日でした。

現場からは以上です。