✂️

Laravel 今更ながら(今後も見据えた) CSRF チェックの外し方

2023/12/13に公開2

前書き

「オイオイ、今更かよ…」と思われそうですが、今回はこれを見て行きたいと思います。新鮮味が無いと面白くありませんので、後半の方では、まだリリースされていない Laravel Ver.11 情報と絡めて行きたいと思います。

本題

という事で、Laravel での CSRF トークンチェックの外し方を3選、ガッと書いてみたいと思います。もちろん普段は、CSRF トークンチェックは外すべきではありませんが、「外部との連携でここだけちょっと外したい」、という状況を想定しています。

その1、VerifyCsrfToken ミドルウェアの $except プロパティに URL を列挙する。

よくあるやり方ですね。ドキュメントにも書かれています。

<?php

namespace App\Http\Middleware;

use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as Middleware;

class VerifyCsrfToken extends Middleware
{
    protected $except = [
        'payment/*',
    ];
}

手軽と言えば、手軽です。

その2、web.php ではなく、api.php の方に書く

web.php ではなく、api.php の方に書けば、最初から CSRF トークンチェックはありませんから、api.php に書くのが妥当な時は、いい選択肢ですね。
とは言え、api.php にはルートが1本だけで、他は全部 web.php に書かれている時は、孤島的な感じでちょっと微妙にもなったり。

その3、web.php にてミドルウェアを外す。

個人的にはちょっと本命で、下記みたいな感じです。

use App\Http\Middleware\VerifyCsrfToken;

Route::post('payment', PaymentController::class)
    ->withoutMiddleware(VerifyCsrfToken::class);

もしグループ化する際は、->group() より前に書いて、

Route::prex('payment')
    ->withoutMiddleware([VerifyCsrfToken::class])
    ->group(function () {
        // ...
});

とこんな感じです。auth とかのようにニックネームは無いので、クラスのフルパスを指定します。

これのメリットは、その1のやり方の場合、URL が変更になった際、$except プロパティの値も変更しないといけないのに対し(← 変更を忘れたりもして…)、こちらの書き方であれば、その心配は要らない事ですね。😊

Laravel Ver.11 では、上記はどうなる?

Laravel Ver.11 は、執筆時点でまだリリースされていませんので、以降は、その後変更無くリリースされたという前提での話になります。
(以降、Laravel Ver.11 を新規でインストールした場合の話です)

まず、デフォルトで、App\Http\Middleware\VerifyCsrfToken のファイルが無くなります。(正確には、他にも色々無くなります)まぁ、無くなるというか、フレームワーク本体側に引っ越したという感じですね。

ですので、上記で見たその1のやり方をするのは、何か小面倒になりそうですね。
その2のやり方は、まぁ、特に変化ないでしょう。

その3のやり方は、App\Http\Middleware\VerifyCsrfToken が無くなる為、フレームワーク側のクラスを指定します。

下記みたいな感じです。(実際は違う💢)

use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken;

Route::post('payment', PaymentController::class)
    ->withoutMiddleware(VerifyCsrfToken::class);

これで大丈夫だろうと思いきや、全く動作しませんでした。😂
「オイオイ、エラーも出ないんだけど」と思っていたら、正解はこうでした。

use Illuminate\Foundation\Http\Middleware\ValidateCsrfToken;

Route::post('payment', PaymentController::class)
    ->withoutMiddleware(ValidateCsrfToken::class);

VerifyCsrfTokenValidateCsrfToken

VerifyCsrfToken はそのまま存在しつつ、影武者的な ValidateCsrfToken ができていて、フレームワーク側では、ValidateCsrfToken で指定されているので、ValidateCsrfToken の方を指定して外してやります。

「いや、名前変えなくてもいいじゃん」と思ったりするのですが、Talor 氏的には、「consistency(一貫性)」の為の変更だそうです。(他の ValidateSignature、ValidatePostSize と名前を合わせたかったらしい)

余談ですが、Laravel にプルリク送る時は、「これは一貫性の為だ」とか、「Ruby on Rails でもこうなっている」とか添えると、マージ率が格段と上がります。筆者調べ(笑

雑感

Laravel Ver.11 では、特にスケルトン(土台)側のファイルが、デフォルトでかなり整理されている感じになりますね。という事で、今までのやり方を Laravel Ver.11 ではどんな風に書くのか色々調べています。

間違い等ありましたらコメント下さい。

Discussion

taterentateren

$except に生のパス書くの嫌で VerifyCsrfToken のコンストラクタに

$this->except[] = route('name');

とか書いてたんですが、ルートからミドルウェアを外す方法良いですね。

nshironshiro

なるほど。コンストラクタを使うという手もあったのですね…。
1箇所で集中管理できるのはいいですね。
勉強になります❗😊