RailsにおけるRate Limitの実装ガイド

# Rate Limitの概要
Rate Limitは、APIへのリクエスト数を制御するためのシステムです。
これは、APIを過剰に使用したり、悪意のある攻撃を防ぐために使用されます。
Rate Limitによって、一定期間内に許可されるリクエストの数が限定されることで、サーバーの過負荷を防ぎ、サービス品質を維持することができます。

## Rate Limitの利点
### サービスの過負荷防止
APIへの過剰なリクエストがサーバーのリソースを圧迫し、サービスが遅くなったり、最悪の場合はダウンしたりすることを防ぎます。
Rate Limitによって、予期せぬトラフィックの急増による障害からAPIを守ります。

### 悪意のある利用の防止
DDoS攻撃など、悪意のあるユーザーによる過剰なリクエストを防ぎます。
これにより、APIが安全に運用されることを確保します。

### 公平なリソースの利用
全てのユーザーに対して公平にAPIリソースへのアクセスを提供します。
一部のユーザーによる過剰なリクエストが他のユーザーの利用機会を奪うことを防ぎます。

### コスト管理
クラウドサービスなどのリソースを使用するAPIでは、リクエスト数に応じてコストが発生します。
Rate Limitによってリクエスト数を制御することで、運用コストの急増を防ぐことができます。

# Rack::AttackによるRate Limitの実装
`Rack::Attack`は、Rackベースのアプリケーションに対する不正なリクエストをフィルタリングし、セキュリティを向上させるためのツールです。
`Rack::Attack`を設定することで、特定の期間内に許可されるリクエストの数を制限し、サービスの乱用を防ぐことができます。

## Rack::Attackの導入と基本設定
Railsプロジェクトで`Rack::Attack`を設定する手順は以下の通りです。

### Railsプロジェクトの作成
まだRailsプロジェクトを作成していない場合は、次のコマンドで新しいプロジェクトを作成します。
```bash
$ rails new myapp
```

### 必要なGemのインストール
プロジェクトのGemfileに`rack-attack`を追加します。
```ruby:Gemfile
gem 'rack-attack'
```
その後、ターミナルで`bundle install`を実行して、変更を適用します。
```bash
$ bundle install
```

### Rack::Attackの設定
`Rack::Attack`の設定ファイルを作成します。
`config/initializers/rack_attack.rb`というファイルを作成し、以下のように記述します。

```ruby:config/initializers/rack_attack.rb
class Rack::Attack
  # 1分間に10リクエストを超えるリクエストをブロック
  throttle('req/ip', limit: 10, period: 1.minute) do |req|
    req.ip
  end

  # ブロックされたリクエストに対するレスポンスのカスタマイズ
  self.throttled_response = lambda do |env|
    [ 429, {}, ['Rate limit exceeded. Try again later.']]
  end
end
```

### ミドルウェアの設定
Railsアプリケーションで`Rack::Attack`をミドルウェアとして使用するために、`config/application.rb`に以下の行を追加します。
```ruby:config/application.rb
module Myapp
  class Application < Rails::Application
    # 中略
    config.middleware.use Rack::Attack
  end
end
```

### Rate Limitのテスト
これで設定は完了です。
サーバーを起動し、指定したRate Limitを超えるリクエストを送信してみて、期待通りに動作するかテストしてみます。
```bash
$ rails server
=> Booting Puma
=> Rails 7.0.8.1 application starting in development 
=> Run `bin/rails server --help` for more startup options
Puma starting in single mode...
* Puma version: 5.6.8 (ruby 3.0.6-p216) ("Birdie's Version")
*  Min threads: 5
*  Max threads: 5
*  Environment: development
*          PID: 34800
* Listening on http://127.0.0.1:3000
* Listening on http://[::1]:3000
Use Ctrl-C to stop
```
`http://127.0.0.1:3000/`にアクセスします。
最初の10回は通常通りの応答が得られます。
![rate-limit.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/458297/0b6fd499-cbf5-a788-7fb3-964702381c1b.png)
11回目以降は制限に達したことを示す応答が返されるようになります。
これにより、Rate Limitの動作を確認できます。
![rate-limit-error.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/458297/1508bdc5-d20c-244a-cef0-40ca2bbdfc63.png)

#### 開発環境でのキャッシュ有効化

`Rack::Attack`は内部的にキャッシュストアを利用してリクエストの制限を行うため、開発環境で`Rack::Attack`を試す際には、Railsのキャッシュ機能を有効化する必要があります。

1. **キャッシュを有効化する**
ターミナルで`rails dev:cache`コマンドを実行して、開発環境でのキャッシュを有効化します。
このコマンドは`tmp/caching-dev.txt`ファイルの作成または削除をトグルし、それによってキャッシュの有効/無効を切り替えます。
2. **確認**
コマンド実行後、ターミナルに`Development mode is now being cached.`または`Development mode is no longer being cached.`というメッセージが表示されます。
これにより、現在のキャッシュの状態（有効または無効）を確認できます。
3. **`Rack::Attack`の動作テスト**
キャッシュを有効化した後、`Rack::Attack`の設定に従ってRate Limitが期待通りに動作するかをテストします。
設定したRate Limitを超えるリクエストを短時間に何度か送信して、適切に制限されるかを確認します。

`Rack::Attack`を使用することで、アプリケーションへの不正なアクセスや過剰なトラフィックから保護することができます。

# メソッドの概要と活用方法
`Rack::Attack`では、リクエストのフィルタリングやレスポンスのカスタマイズに使用できるいくつかの主要なメソッドがあります。
これらのメソッドを利用することで、セキュリティとアクセス制御のための詳細な設定が可能になります。
以下に、主要なメソッドの概要を紹介します。

### `throttle`
特定の条件に基づいてリクエストを制限（スロットル）します。
`limit`と`period`を指定して、その期間内に許可されるリクエストの最大数を定義します。
```ruby:config/initializers/rack_attack.rb
class Rack::Attack
    throttle('requests by ip', limit: 5, period: 60) do |req|
      req.ip
    end
    # その他の設定...
end
```
### `throttled_responder`
Rate Limitによって制限されたリクエストに対するレスポンスをカスタマイズします。
```ruby:config/initializers/rack_attack.rb
class Rack::Attack
    throttled_responder = lambda do |env|
        [429, {}, ['Rate limit exceeded']]
    end
    # その他の設定...
end
```
### `safelist`
特定のIPアドレスや条件にマッチするリクエストを常に許可するために、セーフリストを設定できます。
```ruby:config/initializers/rack_attack.rb
class Rack::Attack
    safelist('allow from localhost') do |req|
        # ここにセーフ条件を記述
        # 例: ローカルホストからのアクセスを許可
        '127.0.0.1' == req.ip || '::1' == req.ip
    end
    # その他の設定...
end
```
### `blocklist`
特定の条件に基づいてリクエストをブロックするために、ブロックリストを設定できます。
```ruby:config/initializers/rack_attack.rb
class Rack::Attack
    blocklist('block suspicious IPs') do |req|
        # ここにブロック条件を記述
        # 例: 疑わしいIPアドレスのリストに含まれている場合
        SUSPICIOUS_IPS.include?(req.ip)
    end
    # その他の設定...
end
```
### `safelisted_responder`
セーフリストに追加されたリクエストに対するレスポンスをカスタマイズします。
ログ記録など特定の目的で利用することがあります。
```ruby:config/initializers/rack_attack.rb
class Rack::Attack
    safelisted_responder = lambda do |env|
        # カスタムレスポンスやログ記録
    end
    # その他の設定...
end
```

### `blocklisted_responder`
ブロックリストに追加されたリクエストに対するレスポンスをカスタマイズします。
```ruby:config/initializers/rack_attack.rb
class Rack::Attack
    blocklisted_responder = lambda do |env|
        [403, {}, ['Blocked']]
    end
    # その他の設定...
end
```

### `cache.store`
`Rack::Attack`が利用する内部キャッシュストアをカスタマイズするための設定です。
デフォルト設定以外に、例えばRedisなどのキャッシュストアを指定できます。
```ruby:config/initializers/rack_attack.rb
class Rack::Attack
    cache.store = ActiveSupport::Cache::RedisCacheStore.new(url: "...")
    # その他の設定...
end
```

これらのメソッドを利用することで、アプリケーションのセキュリティを向上させ、不正アクセスやDDoS攻撃などから保護することができます。

# リクエスト制限の事例

`throttle`メソッドを使用すると、IPアドレス単位以外にも、さまざまな条件でリクエストを制限することが可能です。
以下に、いくつかの実装例を紹介します。

### APIキーごとのRate Limit
APIキーを用いてリクエストを識別し、それに基づいて制限をかけます。
これにより、APIを使用する各ユーザーやアプリケーションに対して、個別の使用制限を設けることができます。
```ruby:config/initializers/rack_attack.rb
class Rack::Attack
    throttle('req/api_key', limit: 5, period: 1.minute) do |req|
        # APIキーはヘッダーまたはクエリパラメータから取得
        req.params['api_key'] || req.headers['X-API-Key']
    end
    # その他の設定...
end
```
### ユーザーごとのRate Limit
認証されたユーザーごとにリクエストを制限します。
これは、ユーザーのセッション情報やトークンに基づいて行うことができます。
```ruby:config/initializers/rack_attack.rb
class Rack::Attack
    throttle('req/user', limit: 10, period: 1.minute) do |req|
        # ユーザー識別子はセッションやトークンから取得
        req.env['warden'].user.try(:id) if req.env['warden'].authenticated?
    end
    # その他の設定...
end
```
### エンドポイントごとのRate Limit
特定のAPIエンドポイントに対するリクエストを制限します。
これにより、データベースへの負荷が高い操作や、外部サービスとの連携が含まれるエンドポイントの使用を制限できます。
```ruby:config/initializers/rack_attack.rb
class Rack::Attack
    throttle('req/endpoint', limit: 10, period: 1.minute) do |req|
        # 特定のエンドポイントへのアクセスを制限
        req.path if req.path.start_with?('/api/sensitive_operation')
    end
    # その他の設定...
end
```
これらの例のように、`Rack::Attack`を使用することで、様々な基準に基づいてリクエストを制限し、アプリケーションを保護することができます。
制限を設定する際には、アプリケーションの要件やユーザーの利便性を考慮しながら、適切なパラメータを選択してください。


# まとめ
`Rack::Attack`を使用することで、RailsアプリケーションにRate Limitを簡単に追加し、アプリケーションの安定性を保つことができます。
また、オプションにより、多様なニーズに対応することができます。

間違っている箇所や疑問に思ったことがあれば、ぜひコメントをください！
最後まで読んでいただき、ありがとうございました。

詳細なコードは、GitHubのリポジトリで公開しています。
興味のある方は是非チェックしてみてください。

https://github.com/GorillaSwe/rate-limit-tutorial-rails