はじめに

クロスサイトリクエストフォージェリは、認証されたユーザーの代理で不正なコマンドが実行される悪意のある攻撃の一種です。幸いなことに、Laravelはアプリケーションをクロスサイトリクエストフォージェリ (CSRF)攻撃から保護するのを容易にします。

脆弱性の説明

クロスサイトリクエストフォージェリに不慣れな場合、この脆弱性がどのように悪用されるかの例を考えてみましょう。あなたのアプリケーションに、認証されたユーザーのメールアドレスを変更するための/user/emailルートがあるとします。このルートは、ユーザーが使用したいメールアドレスを含むemail入力フィールドを期待している可能性が高いです。

CSRF保護がない場合、悪意のあるウェブサイトは、あなたのアプリケーションの/user/emailルートを指すHTMLフォームを作成し、悪意のあるユーザー自身のメールアドレスを送信することができます:

  1. <form action="https://your-application.com/user/email" method="POST">
  2. <input type="email" value="">
  3. </form>
  4. <script>
  5. document.forms[0].submit();
  6. </script>

悪意のあるウェブサイトがページが読み込まれたときに自動的にフォームを送信する場合、悪意のあるユーザーは、あなたのアプリケーションの無防備なユーザーを自分のウェブサイトに誘導するだけで、そのユーザーのメールアドレスがあなたのアプリケーションで変更されます。

この脆弱性を防ぐためには、悪意のあるアプリケーションがアクセスできない秘密のセッション値を持つかどうか、すべての受信POSTPUTPATCH、またはDELETEリクエストを検査する必要があります。

CSRFリクエストの防止

Laravelは、アプリケーションによって管理される各アクティブなuser sessionのためにCSRF「トークン」を自動的に生成します。このトークンは、認証されたユーザーが実際にアプリケーションにリクエストを行っている人物であることを確認するために使用されます。このトークンはユーザーのセッションに保存され、セッションが再生成されるたびに変更されるため、悪意のあるアプリケーションはアクセスできません。

現在のセッションのCSRFトークンは、リクエストのセッションまたはcsrf_tokenヘルパー関数を介してアクセスできます:

  1. use Illuminate\Http\Request;
  2. Route::get('/token', function (Request $request) {
  3. $token = $request->session()->token();
  4. $token = csrf_token();
  5. // ...
  6. });

アプリケーションで「POST」、「PUT」、「PATCH」、または「DELETE」HTMLフォームを定義するたびに、CSRF保護ミドルウェアがリクエストを検証できるように、フォームに隠しCSRF_tokenフィールドを含めるべきです。便利なことに、@csrf Bladeディレクティブを使用して隠しトークン入力フィールドを生成できます:

  1. <form method="POST" action="/profile">
  2. @csrf
  3. <!-- Equivalent to... -->
  4. <input type="hidden" name="_token" value="{{ csrf_token() }}" />
  5. </form>

Illuminate\Foundation\Http\Middleware\ValidateCsrfToken ミドルウェアは、デフォルトでwebミドルウェアグループに含まれており、リクエスト入力のトークンがセッションに保存されているトークンと一致することを自動的に確認します。これらの2つのトークンが一致すると、認証されたユーザーがリクエストを開始していることがわかります。

CSRFトークンとSPA

LaravelをAPIバックエンドとして利用しているSPAを構築している場合は、APIとの認証およびCSRF脆弱性からの保護に関する情報について、Laravel Sanctum documentationを参照してください。

CSRF保護からのURIの除外

時には、CSRF保護から一連のURIを除外したい場合があります。たとえば、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トークンをチェックすることに加えて、Illuminate\Foundation\Http\Middleware\ValidateCsrfTokenミドルウェアは、デフォルトでwebミドルウェアグループに含まれており、X-CSRF-TOKENリクエストヘッダーもチェックします。たとえば、トークンをHTMLmetaタグに保存することができます:

  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は、フレームワークによって生成される各レスポンスに含まれる暗号化されたXSRF-TOKENクッキーに現在のCSRFトークンを保存します。このクッキーの値を使用して、X-XSRF-TOKENリクエストヘッダーを設定できます。

このクッキーは、いくつかのJavaScriptフレームワークやライブラリ(AngularやAxiosなど)が、同一オリジンリクエストのX-XSRF-TOKENヘッダーにその値を自動的に配置するため、主に開発者の便宜のために送信されます。

デフォルトでは、resources/js/bootstrap.jsファイルにはAxios HTTPライブラリが含まれており、X-XSRF-TOKENヘッダーを自動的に送信します。