RAKUS Developers Blog | ラクス エンジニアブログ

株式会社ラクスのITエンジニアによる技術ブログです。

Laravel 8 で強化されたレート制限の新機能を紹介!

f:id:tech-rakus:20201015151740p:plain

こんにちは、MasaKuです。

2020年9月8日に Laravel 8 がリリースされました。

laravel.com

Laravel 8 では セキュリティ周りの強化として Laravel Jetstream という新しいスキャフォールディングが注目されています。

jetstream.laravel.com

しかし、レート制限の新機能にも注目です!

今回はレート制限の解説と新機能部分の説明を行いたいと思います。

レート制限とは

Laravel にはDoS攻撃などの大量アクセス対策として、単位時間当たりに一定以上のアクセスを検知するとアクセス拒否する機能が導入されておりました。

readouble.com

最初にこの機能を知ったときは Apache や Nginx などの Webサーバ側で制限すればいいんじゃないの? と思っていましたが、以下のようなケースではアプリケーション側で制御するほうが都合が良いことに気が付きました。

  1. 特定のアクション(APIなど)に個別の制限をかけたい
  2. 認証を通過したユーザとゲストユーザで制限を分けたい
  3. 特別なオプションを保有しているユーザだけは制限を開放したい

上記はいずれも Laravel 8 以前から実現できましたが、より直感的かつ簡潔に実装できるようになりましたので、Laravel 8 での実装方法について説明したいと思います。

1. 特定のアクション(APIなど)に個別の制限をかける

まず、前提としてレート制限は throttle というミドルウェアを利用して実装します。

このミドルウェアにリミッター名と任意のレート制限を設定します。

app/Providers/RouteServiceProvider.phpconfigureRateLimiting メソッドに以下のような処理を追加します。

<?php

// 一部省略

class RouteServiceProvider extends ServiceProvider
{

// 一部省略

    protected function configureRateLimiting()
    {
        // リミッター名と制限値を設定
        RateLimiter::for('testLimit', function (Request $request) {
            // 1分間に5アクセスまでの制限を追加
            return Limit::perMinute(5);
        });
    }
}

後は、ルーティングを記載している routes/web.php にレート制限をかけたいアクションに対しthrottleミドルウェアを設定するだけです。

throttleミドルウェアには上記で設定したリミッター名を指定します。

<?php

use Illuminate\Support\Facades\Route;
use App\Http\Controllers\TestController;

// アクセス制限をかけたいアクションに上記で設定したリミッター名を設定
Route::middleware(['throttle:testLimit'])->get('/test', [TestController::class, 'index']);

なお、ミドルウェアは複数のアクションをグルーピングできますので、以下のように各アクションに対して同じリミッターを適応することもできます。

<?php
Route::middleware(['throttle:testLimitUser'])->group(function () {
    Route::get('/test1', [TestController::class, 'index']);
    Route::get('/test2', [TestController::class, 'index']);
});

上記のように記載することで /test1/test2 それぞれへのアクセスの合計回数を1分間5回にする制限を付けることができます。

2. 認証を通過したユーザとゲストユーザで制限を分ける

次はログイン認証を通過したユーザとゲストユーザで制限を分けてみます。

先ほど修正した app/Providers/RouteServiceProvider.phpconfigureRateLimiting メソッドを以下の通り修正します。

<?php
RateLimiter::for('testLimit', function (Request $request) {
    return $request->user()
                ? Limit::none()
                : Limit::perMinute(5);
});

ログイン認証を通過しているユーザがアクセスした場合、Request オブジェクトにはユーザ情報がセットされます。

そのため、認証済みのユーザの場合はレートの制限が解放されますが、認証前のユーザの場合は1分間5アクセスまでの制限を付けられます。

3. 特別なオプションを保有しているユーザだけは制限を開放する

認証後のユーザオブジェクトは app\Models\User.php モデルが利用されますので、こちらに独自メソッドを追加することで特別なオプションを持つユーザだけをレート制限から解放することができます。

まずは、以下のようにして app\Models\User.php に独自メソッドを追加します。

<?php

// 省略

class User extends Authenticatable
{

    // 省略

    public function vipCustomer(){
        
        // rakus.co.jp のドメインで登録されているユーザのみ制限を解除する
        if(strpos($this->email,'@rakus.co.jp') !== false){
            return true;
        }
        return false;
    }
}

あとは、リミッター内で上記で設定した独自関数を実行すれば特別なオプションを持つユーザのみアクセス制限を解除することができます。

<?php
RateLimiter::for('testLimit', function (Request $request) {
    return $request->user()->vipCustomer()
                ? Limit::none()
                : Limit::perMinute(5);
});

また、上記に加えて、byでメソッドチェインをすると特定のIPからのアクセスを制限する、などの処理を追加できます。

<?php
RateLimiter::for('testLimit', function (Request $request) {
    return $request->user()->vipCustomer()
                ? Limit::none()
                : Limit::perMinute(5)->by($request->ip());
});

おわりに

いかがでしたでしょうか。

Laravel は非常に多くの機能があるため、こんなこともできるんだ!と驚くような便利な機能に気づいていないことがあります。

新機能のリリースによって、前バージョンよりも使いやすくなったりわかりやすくなることによって、いままで注目されていなかった機能にも気づいてもらいやすくなるのではないかと思います。

そのほかにも、便利な機能がリリースされておりますので、気になった方は是非ご確認ください!

参考サイト

Release Notes - Laravel - The PHP Framework For Web Artisans

Introduction | Laravel Jetstream

ルーティング 8.x Laravel

LaravelのAPIで429 Too Many Requestsが返る - suzu6

Copyright © RAKUS Co., Ltd. All rights reserved.