#はじめに
クロスサイトリクエストフォージェリ(CSRF)は、認証済みユーザーになりすまして不正なコマンドを実行する悪意のある攻撃の一種です。幸いなことに、Laravelはアプリケーションをクロスサイトリクエストフォージェリ(CSRF)攻撃から簡単に保護できます。
#脆弱性の説明
CSRFに馴染みがない場合、この脆弱性がどのように悪用されるか例を挙げて説明します。例えば、アプリケーションに認証済みユーザーのメールアドレスを変更するためのPOSTリクエストを受け付ける/user/emailルートがあるとします。このルートはおそらく、ユーザーが新しく使いたいメールアドレスを含むemail入力フィールドを期待しています。
CSRF保護がない場合、悪意のあるウェブサイトがあなたのアプリケーションの/user/emailルートを指すHTMLフォームを作成し、悪意のあるユーザー自身のメールアドレスを送信することができます。
<form action="https://your-application.com/user/email" method="POST">
<input type="email" value="[email protected]">
</form>
<script>
document.forms[0].submit();
</script>
ページが読み込まれた際に悪意のあるウェブサイトが自動的にフォームを送信すると、悪意のあるユーザーはあなたのアプリケーションのユーザーを騙してそのウェブサイトにアクセスさせるだけで、メールアドレスが変更されてしまいます。
この脆弱性を防ぐために、POST、PUT、PATCH、DELETEの各リクエストに対して、悪意のあるアプリケーションがアクセスできない秘密のセッション値を検査する必要があります。
#CSRFリクエストの防止
Laravelはアプリケーションで管理されている各アクティブなuser sessionに対して自動的にCSRF「トークン」を生成します。このトークンは、認証済みユーザーが実際にリクエストを行っていることを検証するために使われます。トークンはユーザーのセッションに保存され、セッションが再生成されるたびに変わるため、悪意のあるアプリケーションはアクセスできません。
現在のセッションのCSRFトークンは、リクエストのセッションから、またはcsrf_tokenヘルパー関数で取得できます。
use Illuminate\Http\Request;
Route::get('/token', function (Request $request) {
$token = $request->session()->token();
$token = csrf_token();
// ...
});
アプリケーションで「POST」「PUT」「PATCH」「DELETE」のHTMLフォームを定義する際は、CSRF保護ミドルウェアがリクエストを検証できるように、フォームに隠しCSRF _tokenフィールドを含める必要があります。便利な@csrf Bladeディレクティブを使うと、隠しトークン入力フィールドを生成できます。
<form method="POST" action="/profile">
@csrf
<!-- 以下と同等 -->
<input type="hidden" name="_token" value="{{ csrf_token() }}" />
</form>
App\Http\Middleware\VerifyCsrfToken ミドルウェアは、デフォルトでwebミドルウェアグループに含まれており、リクエスト入力のトークンがセッションに保存されたトークンと一致するか自動的に検証します。これらのトークンが一致すると、認証済みユーザーがリクエストを開始したことがわかります。
#CSRFトークンとSPA
LaravelをAPIバックエンドとして利用するSPAを構築している場合は、API認証とCSRF脆弱性対策についてLaravel Sanctumドキュメントを参照してください。
#CSRF保護からURIを除外する
場合によっては、特定のURIをCSRF保護から除外したいことがあります。例えば、Stripeで決済処理を行い、そのWebhookシステムを利用している場合、StripeはCSRFトークンを送信しないため、StripeのWebhookハンドラールートをCSRF保護から除外する必要があります。
通常は、これらのルートをroutes/web.phpファイル内のすべてのルートにApp\Providers\RouteServiceProviderが適用するwebミドルウェアグループの外に配置します。ただし、VerifyCsrfTokenミドルウェアの$exceptプロパティにURIを追加して除外することもできます。
<?php
namespace App\Http\Middleware;
use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as Middleware;
class VerifyCsrfToken extends Middleware
{
/**
* CSRF検証から除外するURIの一覧。
*
* @var array
*/
protected $except = [
'stripe/*',
'http://example.com/foo/bar',
'http://example.com/foo/*',
];
}
便利なことに、テスト実行時はすべてのルートでCSRFミドルウェアが自動的に無効になります。
#X-CSRF-TOKEN
CSRFトークンをPOSTパラメータとしてチェックすることに加え、App\Http\Middleware\VerifyCsrfToken ミドルウェアは X-CSRF-TOKEN リクエストヘッダーも確認します。例えば、トークンをHTMLのmetaタグに格納できます:
<meta name="csrf-token" content="{{ csrf_token() }}">
その後、jQueryのようなライブラリにすべてのリクエストヘッダーにトークンを自動的に追加するよう指示できます。これにより、従来のJavaScript技術を使ったAJAXベースのアプリケーションで簡単かつ便利にCSRF保護ができます。
$.ajaxSetup({
headers: {
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
}
});
#X-XSRF-TOKEN
Laravelは現在のCSRFトークンを暗号化されたXSRF-TOKENクッキーに保存し、フレームワークが生成するすべてのレスポンスに含めます。このクッキーの値を使ってX-XSRF-TOKENリクエストヘッダーを設定できます。
このクッキーは主に開発者の利便性のために送信されます。AngularやAxiosのようなJavaScriptフレームワークやライブラリは、同一オリジンのリクエストで自動的にこの値をX-XSRF-TOKENヘッダーに設定します。
デフォルトで、resources/js/bootstrap.jsファイルにはAxios HTTPライブラリが含まれており、X-XSRF-TOKENヘッダーを自動的に送信します。