【Laravel×Vue.js】CSRF対策をVue側に記述する方法!POST送信が419になる理由

【Laravel×Vue.js】CSRF対策をVue側に記述する方法!POST送信が419になる理由

この記事からわかること

  • Laravel×Vue.jsを使ったSPAでのPOST送信方法
  • 419になってしまった理由と対策
  • Vue.js側にformを実装する方法
  • Laravelの@csrfを設置するには?

index

[open]

\ アプリをリリースしました /

みんなの誕生日

友達や家族の誕生日をメモ!通知も届く-みんなの誕生日-

posted withアプリーチ

PHPのフレームワークであるLaravelとJavaScriptのフレームワークVue.jsを使ってSPA(Single page application)を作っている時にformから値を送信時に419ページになった時の原因や解決法、CSRF対策を施す方法をまとめました。

実現したいことと環境

CSRFとは?

そもそも「CSRF」(Cross-Site Request Forgeries:クロスサイトリクエストフォージェリ)とは、URLをクリック時にそこから悪意を込めたWebサービスへのリクエストが送信されてしまうのが特徴のサイバー攻撃の1つです。

例えばURLをクリックしただけなのに勝手に記事を更新されたり、削除されたり、公開されたりといったPOST送信で操作するようなことを予期せぬデータや手順で操作されてしまうのです。

CSRFの対策として有効なのがPOST送信時のパラメータにトークンを含め、受け取り側のURLでもトークンを発行し、そこに差異がない場合のみ許可するといった「ワンタイムトークン」と呼ばれる対策方法です。

LaravelのCSRF対策

Laravelに常設されているCSRF対策がまさしくこの「ワンタイムトークン」を使用しています。

使用方法は簡単でbladeテンプレート内(XXXX.blade.phpファイル)に設置しているform要素の中に「@csrf」というbladeディレクティブを入れこむだけです。

<form  method="POST" action="URL">
  @csrf
  <label>メールアドレス</label>
  <input name="email" type="text">

  <label>タイトル</label>
  <input name="title"  type="text">

  <label>お問い合わせ内容</label>
  <textarea name="body"></textarea>

  <button type="submit">
    入力内容確認
  </button>
</form>

@csrfディレクティブを入れ込むことでform内に非表示にされたinput要素が作られ、その値にトークンが埋め込まれています。Laravelではそのトークンが正しいものだけを受け付けるようになっているので逆に@csrfを記述しないと受け付けられずエラーが起きてしまいます。

と言ってもページごとにCSRF対策の有効/無効は切り替えることができるので必要なければ無効にすれば@csrfの記述がなくてもエラーが起きないようにすることもできます。

Vue.js内に@csrfを組み込む方法

結論から言うとVue.js内にはLaravelのblade構文(ディレクティブなど)は組み込むことができません。@csrfが使用できるのはbladeテンプレート(XXXX.balde.php)の中だけなのでVue.js(XXXX.vue)のテンプレートを使っている場合は組み込むことができないのです。

なのでVue.js内にformを設置してPOST送信使用とすると以下のように419ページ(有効期限切れ)が表示されてしまいます

Laravelで表示されてしまった419(期限切れ)ページ

これを防ぐ方法は2つ

Vue.jsで行うCSRF対策

Vue.jsで行うCSRF対策を実現する手順

  1. head内にトークンメタタグを配置
  2. Vue.jsのテンプレート内にinput要素を配置
  3. javascriptでメタタグ内のトークンを取得しinput要素の値に充てがう

Vue.js内でCSRF対策を行うにはまずHTMLのhead要素の中に以下のコードを追加します。これでメタタグに一意のトークンを追加することができます。

<meta name="csrf-token" content="{{ csrf_token() }}">

続いてVue.jsのテンプレート内に設置しているform要素の中に以下のコードを追加します。今回はVuexを使っているので$store.state.csrfとしていますが、Vuexを使っていない場合は適当な算出プロパティ(computed)を定義してあげてください。

この値(value)部分にjavascriptコードでメタタグのcontent部分を取得し、変数に格納したものを入れ込みます。

<input type="hidden" name="_token" :value="$store.state.csrf">

Vue.jsテンプレートに組み込んだ場合

<template>
  <form  method="POST" action="URL">
    <!-- @csrfディレクティブは使えない -->
    <!-- CSRF対策用の非表示input要素を定義、値はv-bindして変数を充てがう -->
    <input type="hidden" name="_token" :value="$store.state.csrf">

    <label>メールアドレス</label>
    <input name="email" type="text">

    <label>タイトル</label>
    <input name="title"  type="text">

    <label>お問い合わせ内容</label>
    <textarea name="body"></textarea>

    <button type="submit">
      入力内容確認
    </button>
  </form>
</template>

<script>
export default {
  name: 'csrf-form',
  props: {
  },
  computed: {
    csrf() {
      // Vuex未使用ならこのように定義
    },
  }
};
</script>

メタタグのトークンを取得するのは以下のコードです。説明するまでもないですが、meta[name="csrf-token"]要素のcontent属性の値を取得しています。これを変数に格納して先ほどの値に渡せば完了です。

document.querySelector('meta[name="csrf-token"]').getAttribute('content'),
Vue.use(Vuex);

export default new Vuex.Store({
  strict: true,
  state: {
    //  csrf対策 
    csrf: document.querySelector('meta[name="csrf-token"]').getAttribute('content'),
  },

これで@csrfを記述しなくてもVue.jsで記述したHTMLからのPOST送信で419ページが表示されることなく正常にアクセスすることができます。

LaravelのCSRF対策を不可にする

この方法はCSRF対策を行わないことになるのでformでの送信データをデータベースに保存しないなど重要ではない場合に使える方法です。

LaravelのCSRF対策を行なっている心臓部分は「Http」>「middleware」>「VerifyCsrfToken.php」の中に記述されています。

├── Laravelプロジェクト
│  ├── artisan
│  └── app
│        ├── Console
│        ├── Exception
│        └── Http 
│               ├── Controllers
│               ├── Kernel.php
│               └── Middleware
│                      └── VerifyCsrfToken.php

その中の$exceptと言う変数部分がCSRF対策を無効にする配列となっています。

protected $except = [
  // 例  
    'home',
    'home/*',
]

この配列に無効にしたいアクション(action)先のURLを記述することで指定のactionへのCSRF対策によるワンタイムトークンが無効になり、@csrfを記述しなくても419ページが表示されずにアクセスすることができるようになります。配列になっているので指定したいURLを複数記述することもできますし、ワイルドカード(*)を使って配下全てに適応させることもできます。

またアプリ全体で無効にするには「Http」>「Kernel.php」の中の$middlewareGroups配列内の以下の文をコメントアウト(削除)すれば無効にすることができます。

\App\Http\Middleware\VerifyCsrfToken::class,

まだまだ勉強中ですので間違っている点や至らぬ点がありましたら教えていただけると助かります。

ご覧いただきありがとうございました。

searchbox

スポンサー

ProFile

ame

趣味:読書,プログラミング学習,サイト制作,ブログ

IT嫌いを克服するためにITパスを取得しようと勉強してからサイト制作が趣味に変わりました笑
今はCMSを使わずこのサイトを完全自作でサイト運営中〜

New Article

index