【初心者向け】Laravelで419エラーが発生してPOSTできないとき
いつもご利用ありがとうございます。
この記事には広告が掲載されており、その広告費によって運営しています。
今回は、PHP のフレームワークである Laravel で POST したときに、419 エラーが発生したときの対処法について書いていきます。
環境
MacOS Laravel6
419エラーはどんな時に起こる?
Laravel では、最初からセキュリティ対策のためのシステムがいくつも設定されています。
通常の PHP では、とくに何もしないで POST メソッドを送信することが可能ですが、Laravel ではそれができません。
逆に、何の対策もせずに POST してしまうのが非常にセキュリティ的によくない ため、Laravel の初期に使う設定では、対策なしでは POST できないようになっています。
今回やることは、主に CSRF(クロスサイトリクエストフォージェリ)という攻撃の対策のひとつの、トークンを使った対策となります(エラーの原因はこのトークンの設置がうまくいってないから起きます)
CSRF トークンとは?
CSRF トークンとは、正しいウェブサイトからの POST 送信であるかどうかを、トークン(文字列)によって判断するものになります。
生 PHP では、ログイン時にトークンを生成システムを自作し、セッションに添付するシステムを自作しないとダメなようです。
そして、そのセッションのトークンをサーバー側で照合して、正しくなければ通さないといったシステムになります。
生 PHP では大体
<input type="hidden" name="token" value="ここに生成されたトークンが入る" />
こんな感じで直書きします。
具体的な解決方法
<form method="POST" action="{{ route('post') }}">
//↓を書く!!!
@csrf
大体がこれを忘れていることが原因になってきます。
このコードで生成されたページを覗いてみましょう。
//これは生成されたソースコード
<form method="POST">
<input type="hidden" name="_token" value="vond93ovKGBBXpALpxAu4Ka9V646MW8tm9BvLRFp">
</form>
これがないと、419 エラーになります。
Ajax などの非同期通信の場合
いいね機能など、form ではなく、jQuery などでサクッと送信するような機能を生成する時には、このように form を作成しないと思います。
その時は手順が少し違います。
まず、
<head>
</head>
タグの中に追記します。
<head>
<!-- ほかに色々タグがあるはずです -->
<meta name="csrf-token" content="{{ csrf_token() }}" />
</head>
JavaScript のとき
function post() {
const url = '/post';
fetch(url, {
method: 'POST',
headers: {
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content'),
'Content-Type': 'application/json'
},
body: JSON.stringify({
//送信するやつ
})
})
.then(res => {})
.catch(err => console.log(err));
このように書きます。
headers のところに CSRF-TOKEN を載せます。
これは、meta タグに埋め込まれている CSRF トークンを付与しているので、最初の head の中に記述する必要があります。
jQuery のとき
let url = '/post';
$.ajax({
headers: { 'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content') },
url: url,
type: 'POST',
data: {
}
})
.done(function() {
})
.fail(function(data) {});
}
こんな感じになります。
Vue などで使う、axios のとき、
毎回 token を付与するのが大変なので、post するときには csrf トークンを取得する記述を書きます。
js/bootstrap.js
window.axios = require("axios")
//csrfでaxiosする
window.axios.defaults.headers.common = {
"X-Requested-With": "XMLHttpRequest",
"X-CSRF-TOKEN": document
.querySelector('meta[name="csrf-token"]')
.getAttribute("content"),
}
このような設定をする必要があります。bottstrap.js でする理由としては、毎回記載するのがめんどくさいので一括でやってしまおうということです。
蛇足というか補足というか
この CSRF トークンをどこでシステム化されているかというと Middleware で定義されています。
定義された Middleware を通常使うであろう web.php で使用するというような設定になっています。
app/Http/Kernel.php
protected $middlewareGroups = [
'web' => [
\App\Http\Middleware\EncryptCookies::class,
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
\Illuminate\Session\Middleware\StartSession::class,
// \Illuminate\Session\Middleware\AuthenticateSession::class,
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
// \App\Http\Middleware\VerifyCsrfToken::class,
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],
この部分に VerifyCsrfToken::class という Middleware を使うというように書かれています。
ではこれを外してみます。
<form method="POST" action="{{route('post')}}">
<input name="post" />
<button type="submit">post</button>
</form>
さきほどの@csrf を取りました。
これで POST すると、419エラーが発生しなくなりました。
つまり、このミドルウェアを付けないとダメですよーってことですね。
ちなみに csrf トークンは、StartSession の Middleware を外すと生成されなくなります。
Middleware の中身までは中々追う機会がないので、終えたら記事にでもしてみたいものです。
まとめ
蛇足が多々ありましたが、419エラーの解決方法と、なぜこのようなエラーが発生するのかという、Laravel を触ったことがある人にとっては100000%とおるところの記事を書いてみました。
いかがだったでしょうか?
このエラーがあるおかげで、トークン無しで POST を実装することもないので逆に安心ですね(エラーにはなる けど)
また、フレームワークが便利に付けてくれている機能は、生言語を触る時には「絶対に付けなければならない機能」という解釈もできて、生の言語に対する知識もつくので、便利 × 勉強の両立をさせてくれるのでフレームワーク大好きです。
記事に対する、苦情・修正については Twitter の DM からお願いします。