CSRF保護
イントロダクション
クロスサイトリクエストフォージェリは、認証済みユーザーになりすまして不正なコマンドが実行される悪意のある攻撃の一種です。幸いなことに、Laravelではクロスサイトリクエストフォージェリ(CSRF)攻撃からアプリケーションを簡単に保護できます。
脆弱性の解説
クロスサイトリクエストフォージェリに馴染みがない方のために、この脆弱性がどのように悪用されるかの例を説明します。あなたのアプリケーションに、認証済みユーザーのメールアドレスを変更するためにPOSTリクエストを受け付ける/user/emailルートがあると想像してください。このルートは、ユーザーが使用したいメールアドレスを含むemail入力フィールドを想定しているでしょう。
CSRF保護がない場合、悪意のあるWebサイトは、あなたのアプリケーションの/user/emailルートを指すHTMLフォームを作成し、悪意のあるユーザー自身のメールアドレスを送信できてしまいます。
1<form action="https://your-application.com/user/email" method="POST">3</form>4 5<script>6 document.forms[0].submit();7</script>
もし悪意のあるWebサイトがページ読み込み時に自動的にフォームを送信するなら、悪意のあるユーザーは、あなたのアプリケーションの疑うことを知らないユーザーを自分のWebサイトに訪問させるだけでよく、そうすればあなたのアプリケーションでそのユーザーのメールアドレスが変更されてしまいます。
この脆弱性を防ぐために、受信するすべてのPOST、PUT、PATCH、DELETEリクエストを検査し、悪意のあるアプリケーションがアクセスできない秘密のセッション値があるかを確認する必要があります。
CSRFリクエストの防止
Laravelは、アプリケーションが管理するアクティブなユーザーセッションごとにCSRF「トークン」を自動的に生成します。このトークンは、認証されたユーザーが実際にアプリケーションにリクエストを行っている人物であることを確認するために使用されます。このトークンはユーザーのセッションに保存され、セッションが再生成されるたびに変更されるため、悪意のあるアプリケーションはそれにアクセスできません。
現在のセッションのCSRFトークンは、リクエストのセッションを介して、またはcsrf_tokenヘルパ関数を介してアクセスできます。
1use Illuminate\Http\Request;2 3Route::get('/token', function (Request $request) {4 $token = $request->session()->token();5 6 $token = csrf_token();7 8 // ...9});
アプリケーションで「POST」、「PUT」、「PATCH」、「DELETE」のHTMLフォームを定義するときはいつでも、CSRF保護ミドルウェアがリクエストを検証できるように、フォーム内に非表示のCSRF _tokenフィールドを含める必要があります。便宜上、@csrf Bladeディレクティブを使用して、非表示のトークン入力フィールドを生成できます。
1<form method="POST" action="/profile">2 @csrf3 4 <!-- Equivalent to... -->5 <input type="hidden" name="_token" value="{{ csrf_token() }}" />6</form>
デフォルトでwebミドルウェアグループに含まれているIlluminate\Foundation\Http\Middleware\ValidateCsrfToken ミドルウェアは、リクエスト入力のトークンがセッションに保存されているトークンと一致することを自動的に検証します。これら2つのトークンが一致した場合、認証されたユーザーがリクエストを開始した本人であることがわかります。
CSRFトークンとSPA
APIバックエンドとしてLaravelを利用するSPAを構築している場合は、APIでの認証とCSRF脆弱性からの保護に関する情報について、Laravel Sanctumのドキュメントを参照してください。
CSRF保護からのURIの除外
場合によっては、一連のURIをCSRF保護から除外したいことがあります。たとえば、Stripeを使用して支払いを処理し、そのWebhookシステムを利用している場合、StripeはあなたのルートにどのCSRFトークンを送信すればよいかわからないため、StripeのWebhookハンドラルートをCSRF保護から除外する必要があります。
通常、これらの種類のルートは、Laravelがroutes/web.phpファイル内のすべてのルートに適用するwebミドルウェアグループの外に配置する必要があります。ただし、アプリケーションのbootstrap/app.phpファイル内のvalidateCsrfTokensメソッドにURIを指定することで、特定のルートを除外することもできます。
1->withMiddleware(function (Middleware $middleware) {2 $middleware->validateCsrfTokens(except: [3 'stripe/*',4 'http://example.com/foo/bar',5 'http://example.com/foo/*',6 ]);7})
便宜上、テスト実行時には、CSRFミドルウェアはすべてのルートで自動的に無効になります。
X-CSRF-TOKEN
POSTパラメータとしてCSRFトークンをチェックすることに加えて、デフォルトでwebミドルウェアグループに含まれているIlluminate\Foundation\Http\Middleware\ValidateCsrfTokenミドルウェアは、X-CSRF-TOKENリクエストヘッダもチェックします。たとえば、トークンをHTMLのmetaタグに保存できます。
1<meta name="csrf-token" content="{{ csrf_token() }}">
その後、jQueryのようなライブラリに、すべてのリクエストヘッダにトークンを自動的に追加するように指示できます。これにより、レガシーなJavaScript技術を使用するAJAXベースのアプリケーションに、シンプルで便利なCSRF保護が提供されます。
1$.ajaxSetup({2 headers: {3 'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')4 }5});
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ヘッダを送信します。