LaravelではCSRF攻撃からアプリケーションを保護する仕組みが備わっている。
フォーム送信の際にソースコードでよく見かける「{{ csrf_field() }}」や「@csrf」もその仕組みのもの。
これらは一体何を意味していて、何のために記述する必要があるのかを正しく理解しておきたい。
CSRF攻撃とは
Webアプリケーションの脆弱性を悪用したサイバー攻撃の一種で、悪意のある攻撃者が用意した罠サイトへとユーザを誘導して、本来ユーザの意図していないリクエストを攻撃対象のWebサイトへ送信させる攻撃のこと。
CSRF攻撃の身近な例だと、身に覚えのないSNS上の情報発信・ネットバンキングの不正送金等が挙げられます。
CSRFは「シーサーフ」と呼ばれることが多いですが、正しくは「クロスサイトリクエストフォージェリ(Cross-Site Request Forgery)」と呼びます。
クロスサイトは「サイトを横断する」・フォージェリは「偽装」という意味なので、どんな攻撃なのかのイメージも湧きやすいと思います。
トークン生成によりCSRF対策
Laravelでは、CSRF対策としてトークン生成が採用されている。
トークン生成によって、どのようにCSRF対策されているかというと、
- ユーザがWebアプリケーションにアクセスした際にトークンが自動的に生成され、ユーザ毎のセッションに保存される。
- ユーザがリクエストを送信する際は、生成されたトークンも含めて送信する。
- サーバー側の処理でリクエスト送信されたトークンと予めセッションに保存されていたトークンの値を比較して両者が一致するかどうかを検証する。
- トークンの値が一致すればリクエスト処理を開始。一致しなければエラーを返す。
簡単ではあるが、このような流れのトークン検証を通してCSRF対策を実現している。
2番のリクエストで送信するトークンはサーバ側でトークン検証を行うために、「_token」というname属性を持つ非表示のinputタグをformタグの中に含めて送信する必要がある。
このときに必要なセッションのトークンは、コントローラ内でリクエストのセッションにアクセスすることで取得できる。
// コントローラでの処理
public function(Request $requet) {
$token = $request->session()->token();
...
}
<!-- ビューのformタグ -->
<form method="POST" action="/profile">
<input type="hidden" name="_token" value="{{ $token }}" />
<!-- 以下も同様 -->
<input type="hidden" name="_token" value="{{ csrf_token() }}" />
</form>
上記の例では、コントローラにて、Requestインスタンスを用いてセッションのトークンを取得したが、「csrf_token()へルパ関数」を用いると簡単にセッションのトークンを取得できる。
しかし、更にもっと言えば、「@csrfディレクティブ」を用いると、非表示のトークン付きinputタグを丸ごと生成してくれる。
実際に開発者ツールとかを用いてソースを確認すると@csrfの部分がinputタグに置き換わっていることを確認できる。
(ちなみにLaravel5.5までは、「csrf_field()へルパ関数」を用いると同じことができていたが、Laravel5.6から登場した@csrfディレクティブが現在は主流になっているみたい。)
<form method="POST" action="/profile">
@csrf
<!-- 以下と同様 -->
<input type="hidden" name="_token" value="{{ csrf_token() }}" />
</form>
ちなみに、このCSRF対策のためのトークン検証は「App\Http\Middleware\VerificationCsrfToken」ミドルウェアで行われており、Laravelのデフォルト設定で有効になっている。そのため、特定のサイトアクセスや用途に応じてトークン検証の有効・無効を切り替えることもできる。
トークン送信は特定のHTTPメソッドのみで必要
CSRF対策のためのトークン検証はPOST, PUT, PATCH, DELETEメソッドで有効になっており、リクエスト送信する際はトークンを含める必要がある。
これらのHTTPメソッドは、データベース内容の変更・削除などの悪意あるサイバー攻撃に繋がりやすいためである。
一方で、GETメソッドではトークン検証は実施されないようになっている。
GETメソッドはリクエスト送信しても情報を引き出すだけなので、CSRF攻撃の観点においては悪意のある攻撃に繋がりにくいためだと思われる。
おわりに
formタグの中に記述する「@csrf」は、リクエスト送信を成功させるための特別な記号でもおまじないでもなく、CSRF対策の1つであるトークン検証を行うための重要なものであるということを忘れないようにしておきたい。
参考サイト
- CSRF(クロスサイトリクエストフォージェリ)とは?被害と対策も
- Laravel 8.x CSRF保護