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プロジェクトを作成していない場合は、次のコマンドで新しいプロジェクトを作成します。
$ rails new myapp
必要なGemのインストール
プロジェクトのGemfileにrack-attack
を追加します。
gem 'rack-attack'
その後、ターミナルでbundle install
を実行して、変更を適用します。
$ bundle install
Rack::Attackの設定
Rack::Attack
の設定ファイルを作成します。
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
に以下の行を追加します。
module Myapp
class Application < Rails::Application
# 中略
config.middleware.use Rack::Attack
end
end
Rate Limitのテスト
これで設定は完了です。
サーバーを起動し、指定したRate Limitを超えるリクエストを送信してみて、期待通りに動作するかテストしてみます。
$ 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回は通常通りの応答が得られます。
11回目以降は制限に達したことを示す応答が返されるようになります。
これにより、Rate Limitの動作を確認できます。
開発環境でのキャッシュ有効化
Rack::Attack
は内部的にキャッシュストアを利用してリクエストの制限を行うため、開発環境でRack::Attack
を試す際には、Railsのキャッシュ機能を有効化する必要があります。
-
キャッシュを有効化する
ターミナルでrails dev:cache
コマンドを実行して、開発環境でのキャッシュを有効化します。
このコマンドはtmp/caching-dev.txt
ファイルの作成または削除をトグルし、それによってキャッシュの有効/無効を切り替えます。 -
確認
コマンド実行後、ターミナルにDevelopment mode is now being cached.
またはDevelopment mode is no longer being cached.
というメッセージが表示されます。
これにより、現在のキャッシュの状態(有効または無効)を確認できます。 -
Rack::Attack
の動作テスト
キャッシュを有効化した後、Rack::Attack
の設定に従ってRate Limitが期待通りに動作するかをテストします。
設定したRate Limitを超えるリクエストを短時間に何度か送信して、適切に制限されるかを確認します。
Rack::Attack
を使用することで、アプリケーションへの不正なアクセスや過剰なトラフィックから保護することができます。
メソッドの概要と活用方法
Rack::Attack
では、リクエストのフィルタリングやレスポンスのカスタマイズに使用できるいくつかの主要なメソッドがあります。
これらのメソッドを利用することで、セキュリティとアクセス制御のための詳細な設定が可能になります。
以下に、主要なメソッドの概要を紹介します。
throttle
特定の条件に基づいてリクエストを制限(スロットル)します。
limit
とperiod
を指定して、その期間内に許可されるリクエストの最大数を定義します。
class Rack::Attack
throttle('requests by ip', limit: 5, period: 60) do |req|
req.ip
end
# その他の設定...
end
throttled_responder
Rate Limitによって制限されたリクエストに対するレスポンスをカスタマイズします。
class Rack::Attack
throttled_responder = lambda do |env|
[429, {}, ['Rate limit exceeded']]
end
# その他の設定...
end
safelist
特定のIPアドレスや条件にマッチするリクエストを常に許可するために、セーフリストを設定できます。
class Rack::Attack
safelist('allow from localhost') do |req|
# ここにセーフ条件を記述
# 例: ローカルホストからのアクセスを許可
'127.0.0.1' == req.ip || '::1' == req.ip
end
# その他の設定...
end
blocklist
特定の条件に基づいてリクエストをブロックするために、ブロックリストを設定できます。
class Rack::Attack
blocklist('block suspicious IPs') do |req|
# ここにブロック条件を記述
# 例: 疑わしいIPアドレスのリストに含まれている場合
SUSPICIOUS_IPS.include?(req.ip)
end
# その他の設定...
end
safelisted_responder
セーフリストに追加されたリクエストに対するレスポンスをカスタマイズします。
ログ記録など特定の目的で利用することがあります。
class Rack::Attack
safelisted_responder = lambda do |env|
# カスタムレスポンスやログ記録
end
# その他の設定...
end
blocklisted_responder
ブロックリストに追加されたリクエストに対するレスポンスをカスタマイズします。
class Rack::Attack
blocklisted_responder = lambda do |env|
[403, {}, ['Blocked']]
end
# その他の設定...
end
cache.store
Rack::Attack
が利用する内部キャッシュストアをカスタマイズするための設定です。
デフォルト設定以外に、例えばRedisなどのキャッシュストアを指定できます。
class Rack::Attack
cache.store = ActiveSupport::Cache::RedisCacheStore.new(url: "...")
# その他の設定...
end
これらのメソッドを利用することで、アプリケーションのセキュリティを向上させ、不正アクセスやDDoS攻撃などから保護することができます。
リクエスト制限の事例
throttle
メソッドを使用すると、IPアドレス単位以外にも、さまざまな条件でリクエストを制限することが可能です。
以下に、いくつかの実装例を紹介します。
APIキーごとのRate Limit
APIキーを用いてリクエストを識別し、それに基づいて制限をかけます。
これにより、APIを使用する各ユーザーやアプリケーションに対して、個別の使用制限を設けることができます。
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
認証されたユーザーごとにリクエストを制限します。
これは、ユーザーのセッション情報やトークンに基づいて行うことができます。
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エンドポイントに対するリクエストを制限します。
これにより、データベースへの負荷が高い操作や、外部サービスとの連携が含まれるエンドポイントの使用を制限できます。
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のリポジトリで公開しています。
興味のある方は是非チェックしてみてください。