6
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

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

Last updated at Posted at 2024-02-27

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を追加します。

Gemfile
gem 'rack-attack'

その後、ターミナルでbundle installを実行して、変更を適用します。

$ bundle install

Rack::Attackの設定

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

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に以下の行を追加します。

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回は通常通りの応答が得られます。
rate-limit.png
11回目以降は制限に達したことを示す応答が返されるようになります。
これにより、Rate Limitの動作を確認できます。
rate-limit-error.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

特定の条件に基づいてリクエストを制限(スロットル)します。
limitperiodを指定して、その期間内に許可されるリクエストの最大数を定義します。

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によって制限されたリクエストに対するレスポンスをカスタマイズします。

config/initializers/rack_attack.rb
class Rack::Attack
    throttled_responder = lambda do |env|
        [429, {}, ['Rate limit exceeded']]
    end
    # その他の設定...
end

safelist

特定のIPアドレスや条件にマッチするリクエストを常に許可するために、セーフリストを設定できます。

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

特定の条件に基づいてリクエストをブロックするために、ブロックリストを設定できます。

config/initializers/rack_attack.rb
class Rack::Attack
    blocklist('block suspicious IPs') do |req|
        # ここにブロック条件を記述
        # 例: 疑わしいIPアドレスのリストに含まれている場合
        SUSPICIOUS_IPS.include?(req.ip)
    end
    # その他の設定...
end

safelisted_responder

セーフリストに追加されたリクエストに対するレスポンスをカスタマイズします。
ログ記録など特定の目的で利用することがあります。

config/initializers/rack_attack.rb
class Rack::Attack
    safelisted_responder = lambda do |env|
        # カスタムレスポンスやログ記録
    end
    # その他の設定...
end

blocklisted_responder

ブロックリストに追加されたリクエストに対するレスポンスをカスタマイズします。

config/initializers/rack_attack.rb
class Rack::Attack
    blocklisted_responder = lambda do |env|
        [403, {}, ['Blocked']]
    end
    # その他の設定...
end

cache.store

Rack::Attackが利用する内部キャッシュストアをカスタマイズするための設定です。
デフォルト設定以外に、例えばRedisなどのキャッシュストアを指定できます。

config/initializers/rack_attack.rb
class Rack::Attack
    cache.store = ActiveSupport::Cache::RedisCacheStore.new(url: "...")
    # その他の設定...
end

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

リクエスト制限の事例

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

APIキーごとのRate Limit

APIキーを用いてリクエストを識別し、それに基づいて制限をかけます。
これにより、APIを使用する各ユーザーやアプリケーションに対して、個別の使用制限を設けることができます。

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

認証されたユーザーごとにリクエストを制限します。
これは、ユーザーのセッション情報やトークンに基づいて行うことができます。

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エンドポイントに対するリクエストを制限します。
これにより、データベースへの負荷が高い操作や、外部サービスとの連携が含まれるエンドポイントの使用を制限できます。

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のリポジトリで公開しています。
興味のある方は是非チェックしてみてください。

6
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?