#概要
本記事は、PHPフレームワークLaravel入門 第2版で学習している中の疑問・つまづきの備忘録です。
今回はCSRF対策の際のエラー解決と根本原因についてまとめます。
#発生したエラー
フォーム送信時に以下のエラーが発生しました。
##ソースコード
<form action="/hello" method="post">
<table>
///////中略
<tr>
<th>Message: </th>
<td>
<input type="text" name="msg" value="{{ old('msg') }}">
</td>
</tr>
</table>
</form>
<?php
namespace App\Http\Middleware;
use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as Middleware;
class VerifyCsrfToken extends Middleware
{
protected $except = [
//
];
}
##原因
以下の2点が原因です。
-
index.blade.phpのformタグ内に@csrfディレクティブを記載していない
-
にも関わらず、VerifyCsrfTokenクラスの配列$exceptにCSRF対策から除外するアクション名を記載していない
##解決策1(CSRF対策を行いたい場合)
index.blade.phpのformタグ内に@csrfディレクティブを追記
<form action="/hello" method="post">
<table>
@csrf //これを追記する
///////中略
<tr>
<th>Message: </th>
<td>
<input type="text" name="msg" value="{{ old('msg') }}">
</td>
</tr>
</table>
</form>
##解決策2(CSRF対策を行わない・必要ない場合)
VerifyCsrfTokenクラスの配列$exceptにCSRF対策から除外するアクション名を追記
※この場合formタグに@csrfディレクティブの追記は不要
<?php
namespace App\Http\Middleware;
use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as Middleware;
class VerifyCsrfToken extends Middleware
{
protected $except = [
'hello', //helloアクションではCSRF対策を行わない指定
];
}
##なぜ上記の対応を行わないと419 Page Expiredが発生するのか
こうすればエラーが解消できる!というのは分かりましたが、なぜこのような対応が必要なのでしょうか。
裏側で何が起こっているのか調べてみました。
答えはLaravel 8.x CSRF保護に記載がありました。
アプリケーションで"POST"、"PUT"、"PATCH"、"DELETE" HTMLフォームを定義するときはいつでも、CSRF保護ミドルウェアがリクエストを検証できるように、フォームに非表示のCSRF_tokenフィールドを含める必要があります。便利なように、@csrf Bladeディレクティブを使用して、非表示のトークン入力フィールドを生成できます。
Laravelを使用しないPHPでの開発の際に必要だったtype="hidden"でvalueにトークンを持つinputタグを自動で生成してくれています。
<form method="POST" action="/profile">
@csrf
<!-- Equivalent to... -->
<input type="hidden" name="_token" value="{{ csrf_token() }}" /> //生成されるタグのトークン入力フィールド
</form>
さらに、
webミドルウェアグループへデフォルトで含まれているApp\Http\Middleware\VerificationCsrfTokenミドルウェアは、リクエスト入力のトークンがセッションに保存されたトークンと一致するかを自動的に検証します。この2トークンが一致すれば、認証済みユーザーがリクエストを開始したことがわかります。
つまり今回は
- index.blade.phpのフォームに@csrfディレクティブが記載されておらず、トークンを持つinputタグが生成されていなかった
- VerificationCsrfTokenクラスの配列$exceptでCSRF対策を除外したい当該のフォーム送信時のアクション名が追加されていなかった
- VerificationCsrfTokenで入力トークン検証が失敗した
という理由でフォーム送信時に419 Page Expiredが発生したようです。
#まとめ
CSRF対策する場合は、formタグに@csrfディレクティブを記載する。
しない場合はVerificationCsrfTokenクラスの配列$exceptにCSRF対策から除外するアクション名を追記する。
色々と調べてみると新たな発見があって楽しいですね!
#参考文献