レート制限は、アプリやウェブサイトのリソースを過剰または不適切な使用から保護するために重要です。悪意のある行為の介入、ボットによる攻撃、または見過ごされた脆弱性など、理由は何であれ、リソースの不正使用はアプリケーションへの正当なアクセスを妨げ、深刻なリスクをもたらす可能性があります。
この記事では、LaravelアプリケーションのAPIにレート制限を追加する方法をご紹介します。
LaravelでAPIトラフィックを制限する
レート制限は、アプリケーションのリソースの搾取を緩和するために設計されたメカニズムです。多くの用途がありますが、特に大規模でスケーラブルなシステムのパブリックAPIに有用です。これにより、すべての正当なユーザーがシステムリソースに公平にアクセスできるようになります。
レート制限はまた、セキュリティ、コスト管理、システム全体の安定性にとっても重要です。分散サービス拒否(DDoS)攻撃などのリクエストベースの攻撃を防ぐのに役立ちます。この攻撃は、リクエストを繰り返し送信することで、アプリケーションやウェブサイトサーバーへのアクセスを圧倒し、妨害するものです。
レート制限を実装する方法はいくつかあります。要求者を特徴付ける変数を使用し、誰がどの程度の頻度でアプリケーションにアクセスできるかを決定できます。一般的な変数には、以下のようなものがあります。
- IPアドレス:IPアドレスに基づいてレート制限を実装することで、アドレスごとのリクエスト数を制限できます。この方法は、ユーザーが認証情報を提供せずにアプリケーションにアクセスできる状況で特に有益です。
- APIキー:APIキーを使ってアクセスを制限する場合、リクエスト送信者にAPIキーを提供し、キーごとにレート制限を設定します。この手法では、生成したAPIキーごとに別のアクセスレベルを適用することもできます。
- クライアントID:ユーザーがAPIリクエストのヘッダーや本文に埋め込むことのできるクライアントIDを、事前に生成しておくこともできます。この方法では、クライアントがシステムリソースを独占できないように、IDごとにアクセスレベルを設定可能です。
Laravelミドルウェア
ミドルウェアは、アプリケーションに入るHTTPリクエストを検査し、フィルタリングする便利なメカニズムです。基本的には、アプリケーションとその基礎となるインフラストラクチャの間にあるコードの層で、リソース間の通信を可能にします。
レート制限の実装方法
今回の記事では、Laravel 10フレームワークの既存のミニライブラリAPIを使用して、Laravel Throttleを用いたデモを行います。サンプルのプロジェクトには、コレクション内のブックを管理するために必要な基本的な作成、読み取り、更新、削除(CRUD)と、レート制限のコンセプトをいくつか示すための2つのルートが含まれています。
前提条件
LaravelでのAPI開発の基本に精通していることを前提としています。以下のものがあることを確認してください。
- PHP 8.2、Composer、Laravelがローカルマシンにインストールされ、設定されていること
- 有効なKinstaアカウント
- コードをプッシュするためのGitHub、GitLab、またはBitbucketアカウント
また、MyKinstaを使用して、このAPIをセットアップしてデプロイします。プロジェクトテンプレートに沿って進み、こちらのソースコードから最終結果をプレビューすることができます。
Laravelアプリケーションのセットアップ
- 始めに、プロジェクトテンプレートを複製します。
- 次に、プロジェクトのルートディレクトリに.envファイルを作成し、その中に.env.exampleの内容をコピーします。
- 次に、以下のコマンドを使用してセットアップを完了し、アプリケーションの依存関係をインストールし、アプリキーを生成します。
composer install
php artisan key:generate
このコマンドでアプリキーが.envファイルに自動的に追加されない場合は、php artisan key:generate --show
を実行し、生成されたキーをコピーして、APP_KEY
の値として.envファイルに貼り付けます。
- 依存関係のインストールとアプリキーの生成が完了したら、以下のコマンドを使用してアプリケーションを起動します。
php artisan serve
このコマンドによりアプリケーションを起動し、ブラウザからhttps://127.0.0.1:8000
にアクセスできます。
- URLにアクセスして、Laravelのウェルカムページが表示されることを確認してください。
データベースの設定
MyKinstaでアプリケーションのデータベースを設定しましょう。
- MyKinstaアカウントに移動し「サービスを追加」ボタンをクリックします。
- 「サービスを追加」メニューから「データベース」をクリックし、データベースインスタンスを開始するためのパラメータを設定します。
この説明ではMariaDBを使用しますが、Kinstaで利用可能なLaravelがサポートするデータベースオプションのいずれも選択できます。
- データベースの情報を入力したら、「続行」ボタンをクリックしてプロセスを確定します。
Kinstaでプロビジョニングしたデータベースには、内部接続パラメータと外部接続パラメータがあります。同じKinstaアカウント内でホストしているアプリケーションには内部接続を使用し、外部との接続には外部接続パラメータを使用します。ここでは、外部データベース認証情報を使用します。
- Laravelの.envで以下のコード(スクリーンショットにあるような実際の外部認証情報をコピーして貼り付け)を使用してください。
DB_CONNECTION=mysql
DB_HOST=your_host_name
DB_PORT=your_port
DB_DATABASE=your_database_info
DB_USERNAME=your_username
DB_PASSWORD=your_password
- データベースの認証情報を入力した後、以下のコマンドを使用しデータベースのマイグレーションを適用して接続をテストします。
php artisan migrate
すべてが正しく機能すれば、以下のような応答が表示されるはずです。
- 次に、以下のコマンドを使用して、アプリケーションルートをリストアップし、すでに実装されているルートを確認します。
php artisan route:list
すると、以下のように利用可能なAPIエンドポイントが表示されます。
- アプリケーションを起動し、すべてが正常に動作することを確認してください。PostmanやCURLのようなツールを使って、ターミナルからエンドポイントをテストすることができます。
Laravelアプリケーションでレート制限する方法
Laravelアプリケーションには、複数のレート制限テクニックがあります。IPアドレスのセットをブロックしたり、ユーザーのIPアドレスやuser_idに基づいて期間ベースのリクエスト制限を実施したりできます。それでは、それぞれの方法を見ていきましょう。
- 以下のコマンドを使用して、Laravel Throttleパッケージをインストールします。
composer require "graham-campbell/throttle:^10.0"
- また、
vendor configurations
ファイルを公開することで、Laravel Throttleの設定を調整できます。
php artisan vendor:publish --provider="GrahamCampbellThrottleThrottleServiceProvider"
IPアドレスをブロックする方法
レート制限のテクニックの1つで、指定したIPアドレスのセットからのリクエストをブロックすることができます。
- 始めに、必要となるミドルウェアを作成します。
php artisan make:middleware RestrictMiddleware
- 次に、作成したapp/Http/Middleware/RestrictMiddleware.phpミドルウェアファイルを開き、
handle
関数のコードを以下に置き換えます。ファイルの一番上にあるインポートのリストにuse App;
が追加されていることを確認してください。
$restrictedIps = ['127.0.0.1', '102.129.158.0'];
if(in_array($request->ip(), $restrictedIps)){
App::abort(403, 'Request forbidden');
}
return $next($request);
- app/Http/Kernel.phpファイルで、
middlewareAliases
の配列を以下のように変更して、このミドルウェアアプリのエイリアスを作成します。protected $middlewareAliases = [ . . . 'custom.restrict' => AppHttpMiddlewareRestrictMiddleware::class, ];
- 次に、このミドルウェアをroutes/api.phpファイルの
/restricted-route
に適用しテストを行います。
Route::middleware(['custom.restrict'])->group(function () { Route::get('/restricted-route', [BookController::class, 'getBooks']); });
これが問題なく動作すると、ミドルウェアにより、
$restrictedIps
の配列にあるIP(127.0.0.1
と102.129.158.0
)からのすべてのリクエストがブロックされます。これらのIPからのリクエストに対しては、以下のように403 Forbiddenレスポンスが表示されます。Postmanの/restricted-route GETエンドポイントに対する403 Forbiddenレスポンス IPアドレスでリクエストに制限をかける方法
次に、IPアドレスを使ってリクエストに制限を設ける方法です。
- routes/api.phpの
/book
エンドポイントのGET
とPATCH
ルートにThrottleミドルウェアを適用します。
Route::middleware(['throttle:minute'])->group(function () { Route::get('/book', [BookController::class, 'getBooks']); }); Route::middleware(['throttle:5,1'])->group(function () { Route::patch('/book', [BookController::class, 'updateBook']); });
- また、app/Providers/RouteServiceProviderファイルの
configureRateLimiting
関数を、上記のルートに追加したミドルウェアの情報に変更します。
… RateLimiter::for('minute', function (Request $request) { return Limit::perMinute(5)->by($request->ip()); });
この設定により、
/book GET
エンドポイントへのリクエストは1分あたり5件に制限されます。Postmanの/book GETエンドポイントに対するレスポンス(429 Too Many Requests) ユーザーIDとセッションに基づきリクエストに制限をかける方法
user_id
とsession
パラメータを使ってレート制限を行うには、app/Providers/RouteServiceProviderファイルのconfigureRateLimiting
関数を、以下に変更します。
... RateLimiter::for('user', function (Request $request) { return Limit::perMinute(10)->by($request->user()?->id ?: $request->ip()); }); RateLimiter::for('session', function (Request $request) { return Limit::perMinute(15)->by($request->session()->get('key') ?: $request->ip()); });
- 最後に、このコードを
routes/api.php
ファイルの/book/{id} GET
と/book POST
ルートに適用します。
Route::middleware(['throttle:user'])->group(function () { Route::get('/book/{id}', [BookController::class, 'getBook']); }); Route::middleware(['throttle:session'])->group(function () { Route::post('/book', [BookController::class, 'createBook']); });
このコードはそれぞれ、
user_id
とsession
を使ってリクエストを制限します。Throttleのその他のメソッド
Laravel Throttleには、レート制限の実装を細かくコントロールするためのメソッドがいくつか用意されています。例えば以下の通りです。
attempt
:エンドポイントをヒット、ヒットカウントをインクリメントし、設定されたヒット制限を超えたかどうかを示すブール値を返すhit
:Throttleをヒットし、ヒットカウントを増加させ、$this
を返し、別の(オプションの)メソッド呼び出しを有効にするclear
:Throttleカウントをゼロにリセットし、$this
を返す(必要に応じて別のメソッドを呼び出すことができる)count
:Throttleの総ヒット数を返すcheck
:Throttleのヒット制限を超えたかどうかを示すブール値を返す
- レート制限についてこれらのメソッドを使用するために、以下のコマンドを使用してCustomMiddlewareというミドルウェアアプリを作成してみます。
php artisan make:middleware CustomMiddleware
- 次に、app/Http/Middleware/CustomMiddleware.phpに新しく作成したミドルウェアファイルに以下のインポートファイルを追加します。
use GrahamCampbellThrottleFacadesThrottle; use App;
- 次に、
handle
メソッドの内容を以下のコードに置き換えます。
$throttler = Throttle::get($request, 5, 1); Throttle::attempt($request); if(!$throttler->check()){ App::abort(429, 'Too many requests'); } return $next($request);
- app/Http/Kernel.phpファイルで、
middlewareAliases
の配列を以下のように変更して、このミドルウェアアプリのエイリアスを作成します。
protected $middlewareAliases = [ . . . 'custom.throttle' => AppHttpMiddlewareCustomMiddleware::class, ];
- 次に、このミドルウェアをroutes/api.phpファイルの
/custom-route
に適用します。
Route::middleware(['custom.throttle'])->group(function () { Route::get('/custom-route', [BookController::class, 'getBooks']); });
先ほど実装したカスタムミドルウェアは、スロットルリミットを超えたかどうかを
check
メソッドを使って確認します。制限を超えた場合、429エラーが返されます。そうでなければ、リクエストの続行を許可することになります。アプリケーションをKinstaにデプロイする方法
Laravelアプリケーションにレート制限を実装する方法を理解したところで、今度はアプリをKinstaにデプロイして、一般にアクセスできる状態にしてみましょう。
- 用意したコードをGitHub、GitLab、またはBitbucketにプッシュします。
- Kinstaから、「サービスを追加」ボタンをクリックし、一覧から「アプリケーション」を選択します。GitアカウントをKinstaアカウントに紐付け、デプロイするリポジトリを選択します。
- 「基本情報」で、アプリケーションに名前を付け、希望のデータセンターを選択します。また、必要なアプリケーション環境変数を追加してください。これはローカルの.envファイルに存在する変数に対応するものになります(
APP_KEY
とデータベース設定用の変数)。
アプリケーションデプロイに際しMyKinstaで設定を指定していく - 「続行」ボタンをクリックして、ビルド環境変数を選択します。必要なパラメータが自動で入力されるので、デフォルトの値のままで問題ありません。
- 「プロセス」タブでは、デフォルトの値のままにするか、プロセスの名前を入力できます。また、このタブでPodとインスタンスサイズを選択することもできます。
- 最後に、「お支払い」タブに選択した項目の概要が表示されます。ご希望のお支払い方法を追加し、プロセスの確定を行います。
- 完了したら、「アプリケーション」タブをクリックして、デプロイ済みアプリケーション一覧を表示します。
- アプリケーション名をクリックすると、以下のスクリーンショットのようにデプロイメントの詳細が表示されます。ここにあるアプリケーションのURLを使用してアクセスできます。
MyKinstaでデプロイが終わり詳しい情報が表示される アプリケーションのテスト方法
- アプリケーションをローカルでテストするには、
php artisan serve
コマンドを使用できます。
このコマンドは、アプリケーションのブラウザを
http://localhost:8000
でアクセスできるようにするものです。レート制限機能をトリガーするために繰り返し呼び出すことで、ここからレート制限を実装したAPIエンドポイントをテストすることができます。KinstaサーバーによりAccess Forbiddenのレスポンスが表示される可能性があります。これは、アプリケーションの配信方法をKinstaに指示する設定を行っていないためです。以下の調整を行いましょう。
- アプリのルートディレクトリに
.htaccess
ファイルを作成し、次のコードを記述します。
<IfModule mod_rewrite.c> RewriteEngine On RewriteRule ^(.*)$ public/$1 [L] </IfModule>
- これをGitHubにプッシュすると、Kinstaにより自動で再デプロイが実行されます。
- 先ほどのURLを使ってアプリケーションを開き、Laravelのウェルカムページが表示されていることを確認してください。
Postmanを使用してレート制限を実装したAPIエンドポイントを、設定した制限に達するまで繰り返しコールしてテストすることができます。制限を超えると、429 Too Many Requestsレスポンスが出されます。
まとめ
LaravelのAPIにレート制限機能を統合すると、ユーザーによるアプリケーションのリソース消費をうまく管理することができます。レート制限を行うことで、過不足なく信頼性の高いユーザーエクスペリエンスを提供可能です。また、アプリケーションの基礎となるインフラの機能と効率を確保する上でも有用でしょう。
Kinstaでは数々の記事を執筆しています。Laravelはもちろんのこと、その他のウェブテクノロジーに関するコンテンツが盛りだくさんです。高品質かつお手頃なホスティングサービスは、アプリケーションを支える大事な土台です。Kinstaを是非ともご検討ください。
- 次に、このミドルウェアをroutes/api.phpファイルの
コメントを残す