Laravel

【初心者向け】Laravelで419エラーが発生してPOSTできないとき

いつもご利用ありがとうございます。このブログは、広告費によって運営されています。

オススメ本
Web技術を勉強するなら、かなりオススメの雑誌です。毎月新しい発見があります。ついに最終号・・・、みなさん買いましょう!!
読んで損することはない名著。命名で悩むことが多い人はこの本がオススメです。

今回は、PHP のフレームワークである Laravel で POST したときに、419 エラーが発生したときの対処法について書いていきます。

⇨ Laravel 記事の目次はこちら

環境

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 の中身までは中々追う機会がないので、終えたら記事にでもしてみたいものです。

オススメ本
Web技術を勉強するなら、かなりオススメの雑誌です。毎月新しい発見があります。ついに最終号・・・、みなさん買いましょう!!
読んで損することはない名著。命名で悩むことが多い人はこの本がオススメです。

まとめ

蛇足が多々ありましたが、419エラーの解決方法と、なぜこのようなエラーが発生するのかという、Laravel を触ったことがある人にとっては100000%とおるところの記事を書いてみました。

いかがだったでしょうか?

このエラーがあるおかげで、トークン無しで POST を実装することもないので逆に安心ですね(エラーにはなるけど)

また、フレームワークが便利に付けてくれている機能は、生言語を触る時には「絶対に付けなければならない機能」という解釈もできて、生の言語に対する知識もつくので、便利 × 勉強の両立をさせてくれるのでフレームワーク大好きです。

記事に対する、苦情・修正については Twitter の DM からお願いします。

Laravel で app.js や app.css が 404NOT FOUND するときの解決方法