Laravel CSRF関連
作成日:2019-03-29, 更新日:2019-04-02
基本
・get送信だとCSRFはあっても無くても大丈夫
・post送信だとCSRFが無いと419エラー
ワンタイム、固定
ワンタイムトークンと固定トークンのどちらが正解なのかはよく分からん。
※考え方によってどっちが正解なのか変わるっぽい。
ひとまずLaravelのCSRFは固定っぽい。
そのためformの多重送信の対策が必要。
form送信
下記のどちらかを用意する
▼csrfをセットしてformで投げる1
<form method="post"> @csrf <input type="submit"> </form>
※「@csrf」がinputタグごと生成してくれる
▼csrfをセットしてformで投げる2
<form method="post">
<input type="hidden" name="_token" value="{{ csrf_token() }}" />
<input type="submit">
</form>
※「csrf_token()」がCSRFの値を用意してくれる
多重送信の対策
ボタンクリックされても大丈夫なようにするってヤツ。
・JS側でボタンを無効化
・トークンを再生成
・固定のトークンをワンタイムに変更
JS側でボタンを無効化
▼ボタンクリック後の挙動
・最終的にページ遷移するようなヤツは「無効」でOK
・ページ遷移しないようなヤツは「無効」と条件によって「再有効」が必要
トークンを再生成
▼対象のコントローラーの処理内で「regenerateToken()」を行う
$request->session()->regenerateToken(); // \Session::regenerateToken(); // ←もしくはコッチ
ほとんどのサイトがこれで「ボタン連打による多重送信を防ぐ」とかいいながらまったくもって防いでいない・・・。
ワンタイムトークンの実装
・LaravelのCSRFトークンをワンタイムトークン化
→私には難易度が高く、挫折。
疑似ワンタイムトークンの実装
上述のワンタイムトークンの実装を元に自分なりに用意
▼内容
・CSRFのチェックが終わったらトークンを再生成
▼手順
・ミドルウェアの作成
・ミドルウェアの登録
▼ミドルウェアの作成:app\Http\Middleware\OneTimeCsrfToken.php
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Support\Facades\Cache;
class OneTimeCsrfToken {
public function handle($request, Closure $next) {
if ($request->method() === 'POST')
// POSTのときだけトークンリフレッシュ
$request->session()->regenerateToken();
}
return $next($request);
}
}
▼ミドルウェアの登録:app\Http\Kernel.php
~ 略 ~
protected $middlewareGroups = [
'web' => [
~ 略 ~
\App\Http\Middleware\VerifyCsrfToken::class,
\App\Http\Middleware\OneTimeCsrfToken::class, // ←VerifyCsrfTokenの下に追加
~ 略 ~
],
~ 略 ~
Form:多重送信対策(2019-03-29):ボタン無効化+トークン更新
コレで万全なのかは分からない。
※ミドルウェアは上記と同じ内容
▼ビュー
<form method="post">
<input type="hidden" name="_token" value="{{ csrf_token() }}" />
<input type="button">
</form>
※「type="submit"」じゃなく「type="button"」を使う
<script>
$('form').on('click', '[type="button"]', function(){
$('[type="button"]').prop('disabled', true);
$(this).parents('form').submit();
});
</script>
※formやjqueryのトコは必要に応じて修正。
▼ミドルウェアの作成:app\Http\Middleware\OneTimeCsrfToken.php
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Support\Facades\Cache;
class OneTimeCsrfToken {
public function handle($request, Closure $next) {
if ($request->method() === 'POST')
// POSTのときだけトークンリフレッシュ
$request->session()->regenerateToken();
}
return $next($request);
}
}
▼ミドルウェアの登録:app\Http\Kernel.php
~ 略 ~
protected $middlewareGroups = [
'web' => [
~ 略 ~
\App\Http\Middleware\VerifyCsrfToken::class,
\App\Http\Middleware\OneTimeCsrfToken::class, // ←VerifyCsrfTokenの下に追加
~ 略 ~
],
~ 略 ~