質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.03%

[Laravel] CsrfTokenが正常で無い場合 → "419 Page Expired"のページが出ないようにしたい

解決済

回答 2

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 896

kazoogon

score 269

環境

Laravel5.8

作成中の機能

「お問い合わせフォーム」
名前や年齢をFormでControllerに送信、メールを送る。

実現したいこと

・二重送信
・Form送信完了後、戻ってリロード
→上記行った場合に自分の作ったviewを表示させるようにしたい

試したこと

「Csrf tokenを検証」

public function form(Request $request) {
    if($request->session()->token() !== $request->input('_token')) {
        return view('Errors/hoge'); //自分で作成したviewを表示したい
    }
    $this->sendMail(); //private functionでメール送る
  $request->session()->regenerateToken();//tokenの作成

    return view('complete'); //完了画面に行く
}


これだと2重で送信ボタンclickなどすると、自分の作ったエラーviewが表示されず画像の様なLaravelにデフォルトである419のエラーページが表示されてしまいます。
イメージ説明
よろしくお願いいたします。

追記

//form submitするview
//このページは確認画面なので、inputなどはありません
<form method="POST" action="/submit">
    @csrf
    <table>
        <tr>
            <th>名前</th>
            <td>{{$hoge->name}}</td>
        </tr>
        <tr>
            <th>メールアドレス</th>
            <td>{{$hoget->email}}</td>
        </tr>
    </table>
    <button name="submit">送信</button>
</form>

追記(aro10さんの回答に対しての追記)

1,フォーム表示時に、 セッションにフォーム用のキーでランダムデータを格納し、フォームのhidden入力欄に代入しておく

form内にLaravelの@csrfを挿入してあります。

2,フォーム送信時に送信内容と格納したセッションデータを検証、不一致の場合は専用のエラーページへ

if($request->session()->token() !== $request->input('_token')) {

の部分でこれをしていると思いますが、Laravel defaultの419のページが出る。という状態です

追記(進捗状況)

App\Exceptions\Handler.phpを修正し、自分でExceptionのカスタマイズをした(「Handlerを書き換える」というのを今まではlaravel\framework\src\Illuminate\Foundation\Exceptions\Handler.phpの方だと勘違いしていた)

//App\Exceptions\Handler.php
public function render($request, Exception $exception)
{
    if ($exception instanceof TokenMismatchException) {
        abort(401);
    }
}


**問題点
①abortの代わりにreturn response()->view('errors.401');を使用するとview画面が表示されるが、abort(401)だと画面は真っ白 (できるだけabortを使用したい)

・送信完了後、戻ってreloadする場合には対応できる
・ダブルクリックされた場合は、メールが送られた後にエラー画面が表示されてしまう (JSでなくPHPの対応方法はあるか?)

追記(ほぼ解決)

上記render関数一番下に下記コードを挿入(というか元々あったやつ)すると401のviewが表示された

return parent::render($request, $exception);

・二重送信は「メールが送られた後にエラー画面が表示されてしまう」のままですが、こちらはJSで対処したいと思います

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • fumifumi_60

    2019/06/14 09:47 編集

    質問の再確認ですが、自分で作ったエラーメッセージで
    既存の419を入れ替えたいということでよろしいですか?

    もしくは、二重送信を防止したいというニュアンスでしょうか?

    キャンセル

  • m.ts10806

    2019/06/14 10:38

    view側のコードもご提示ください。

    キャンセル

  • kazoogon

    2019/06/14 15:55

    fumifumi_60さん
    二重送信を防止したうえで、自分の作ったエラーメッセージを表示したい。ということです

    キャンセル

  • m.ts10806

    2019/06/14 16:14

    提示していただきたいのはformが書いてある方のviewです…

    キャンセル

回答 2

checkベストアンサー

+1

https://readouble.com/laravel/5.8/ja/errors.html
これを参考によしなに修正する事
LaravelデフォルトのCSRFエラーの場合はTokenMismatchExceptionが投げられている

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/06/14 16:29

    回答ありがとうございます。
    installしたLaravelそのものを変更する必要が有るってことですよね??
    例 Foundation/Exceptions/Handler.php

    キャンセル

  • 2019/06/14 16:43

    どの範囲をそのものって言ってるのか分からないけど、例外ハンドラーは変更されること前提に考えられてると思うけど
    触られたくないところならvendorの中に入ってるでしょ
    それも嫌ならlaravelのcsrf対策自体を切ってしまって自前でトークン生成と制御をすればいいですよ
    csrf対策のプログラムの読み込みはkernelのmiddlewareに書いてあるのでコメントアウトすればいいですよ

    キャンセル

  • 2019/06/14 21:48

    追記(進捗状況)に記載しましたが、Handler.phpを勘違いしていました汗
    まだ問題点ありますので、何か心当たりありましたらご連絡お願い致します。

    キャンセル

+1

CSRFは意図しないリクエストを防ぐシステム全体を覆う仕組みなので、
戻るボタンを押して再表示したフォームの二重送信を防ぐという用途ならば個別に用意したものを使う方が良いです。

一例として

  1. フォーム表示時に、 セッションにフォーム用のキーでランダムデータを格納し、フォームのhidden入力欄に代入しておく
  2. フォーム送信時に送信内容と格納したセッションデータを検証、不一致の場合は専用のエラーページへ
  3. フォーム送信完了後にセッションにフォーム用のキーを削除

などが考えられます。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/06/14 16:11

    ご回答ありがとうございます、「追記(aro10さんの回答に対しての追記)」を記載致しました。ご確認お願いいたします

    キャンセル

  • 2019/06/14 16:26

    これをやってしまうと、CSRF用のトークンが更新されてしまうのでコメントアウトして、
    $request->session()->regenerateToken();

    sesssion(["form"=>"ランダムな値")のように、普通のセッションデータで使うと良いかと思います。

    キャンセル

  • 2019/06/14 21:47

    追記(進捗状況)に記載しましたが、Handler.phpを修正してできそうです(私の勘違いがありました汗)
    まだ問題点ありますので、何か心当たりありましたらご連絡お願い致します。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 90.03%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる