はじめに
クロスサイトリクエストフォージェリは、認証されたユーザーの代理で不正なコマンドが実行される悪意のある攻撃の一種です。幸いなことに、Laravelはアプリケーションをクロスサイトリクエストフォージェリ (CSRF)攻撃から保護するのを容易にします。
脆弱性の説明
クロスサイトリクエストフォージェリに不慣れな場合、この脆弱性がどのように悪用されるかの例を考えてみましょう。あなたのアプリケーションに、認証されたユーザーのメールアドレスを変更するための/user/email
ルートがあるとします。このルートは、ユーザーが使用したいメールアドレスを含むemail
入力フィールドを期待している可能性が高いです。
CSRF保護がない場合、悪意のあるウェブサイトは、あなたのアプリケーションの/user/email
ルートを指すHTMLフォームを作成し、悪意のあるユーザー自身のメールアドレスを送信することができます:
<form action="https://your-application.com/user/email" method="POST">
<input type="email" value="">
</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
<!-- Equivalent to... -->
<input type="hidden" name="_token" value="{{ csrf_token() }}" />
</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を提供することで、特定のルートを除外することもできます:
->withMiddleware(function (Middleware $middleware) {
$middleware->validateCsrfTokens(except: [
'stripe/*',
'http://example.com/foo/bar',
'http://example.com/foo/*',
]);
})
便利なことに、テストを実行しているときは、すべてのルートに対してCSRFミドルウェアが自動的に無効になります。
X-CSRF-TOKEN
POSTパラメータとしてCSRFトークンをチェックすることに加えて、Illuminate\Foundation\Http\Middleware\ValidateCsrfToken
ミドルウェアは、デフォルトでweb
ミドルウェアグループに含まれており、X-CSRF-TOKEN
リクエストヘッダーもチェックします。たとえば、トークンをHTMLmeta
タグに保存することができます:
<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は、フレームワークによって生成される各レスポンスに含まれる暗号化されたXSRF-TOKEN
クッキーに現在のCSRFトークンを保存します。このクッキーの値を使用して、X-XSRF-TOKEN
リクエストヘッダーを設定できます。
このクッキーは、いくつかのJavaScriptフレームワークやライブラリ(AngularやAxiosなど)が、同一オリジンリクエストのX-XSRF-TOKEN
ヘッダーにその値を自動的に配置するため、主に開発者の便宜のために送信されます。
デフォルトでは、resources/js/bootstrap.js
ファイルにはAxios HTTPライブラリが含まれており、X-XSRF-TOKEN
ヘッダーを自動的に送信します。