レート制限に関するベストプラクティス

レート制限に関するベストプラクティス

概要

AuthleteのAPIエンドポイントを使用する際、サービス品質の低下や、自らの過剰なリクエストによってDDoS状態に陥る事態の回避、およびピーク時における継続的な運用の確保のために、Authleteのレート制限ポリシーを考慮した上でアプリケーションを開発する必要があります。以下に、Authleteユーザーの皆様に対して当社が推奨する技術的ベストプラクティスを記載します。

クエリエンドポイントに対するインテリジェントキャッシングの実装

キャッシングは、不要なAPIコールとレート制限違反に対する主要な防御手段です。Authleteの場合、定期的に使用されるエンドポイントの中に、レスポンス内容がほとんど変化しないものも含まれており、パフォーマンスの向上あるいは外部要因によるレート制限のリスク軽減を実現するためにキャッシュを使用することが有効です。以下がアプリケーションのキャッシングを設計する際に考慮すべきエンドポイントの一覧になります。

エンドポイント 用途 詳細
サービス設定の取得
/api/{serviceId}/service/configuration
OpenID ConnectおよびOpenID Connect Discovery 1.0をサポートするサービスの設定を行うために呼び出されるAPI 多くのOpenID SDKおよびライブラリは、設定を簡略化するためにディスカバリーエンドポイントを使用します。これらのライブラリがWebアプリケーションやウェブサイトのコンポーネントとしてデプロイされる際、OpenIDプロバイダーが大量のリクエストを受信する可能性があります。サービス設定がそれほど頻繁に変更されるものではないため、このエンドポイントを安全にキャッシュすることができます。
JWKセットの取得
/api/{serviceId}/service/jwks/get
OpenID ConnectをサポートするサービスがJWKセット情報を公開しなければならないJWKセットエンドポイントの実装のために呼び出されるAPI 多くのOpenID SDKおよびライブラリは、OpenIDプロバイダーの公開鍵を取得し設定を簡略化するためにJWKsエンドポイントを照会します。モバイルやWebアプリの場合、OpenIDプロバイダーのJWKsエンドポイントへのアクセスが増加します。鍵のローテーションは数週間から数ヶ月単位で行われるため、このエンドポイントを安全にキャッシュすることができます。
クライアントの取得
/api/{serviceId}/client/get/{clientId}
クライアントの詳細と設定を取得するAPI 前述のエンドポイントとは異なり、このエンドポイントは特定のエンドポイントを直接サポートするものではありませんが、一部のプロセスにおいて一般的に使用されます。クライアントメタデータが通常のフローの一部として使用される場合、このエンドポイントのレスポンスをキャッシュすることを推奨します。
Verifiable Credential Issuerメタデータの取得
/api/{serviceId}/vci/metadata
OpenID for Verifiable Credentialsをサポートするサービスのメタデータエンドポイントの実装のために呼び出されるAPI メタデータエンドポイントはウォレットによって使用されるよう設計されているため、その使用状況によって利用が変動します。他のサービス設定エンドポイントと同様に、レスポンス内容はそれほど頻繁に変更されないため、安全にキャッシュすることが可能になります。
JSON Web Keyセットの取得
/api/{serviceId}/vci/jwks
SD-JWTが導入されているサービスのJWKsエンドポイントの実装のために呼び出されるAPI OpenIDのJWKsと同様に、公開鍵はウォレットによって定期的にリクエストされるため、大量のリクエストが発生する可能性があります。通常、鍵のローテーションは数週間から数ヶ月単位で行われるため、このエンドポイントを安全にキャッシュすることができます。
イントロスペクションリクエストの処理
/api/{serviceId}/auth/introspection
クライアントアプリケーションから提示されたアクセストークンの情報を取得するために、認可サーバーの導入において保護されたリソースエンドポイントの実装のために呼び出されるAPI トークンに関連付けられた情報の取得は、アプリケーションとリソースサーバーの通信方法によっては複数回発生する場合があります。一部のケースにおいては、Authleteのイントロスペクションエンドポイントのレスポンスをキャッシュすることによって、リソースサーバーAPIのレスポンスパフォーマンスが向上し、Authleteサービスへの負荷を軽減することができます。
詳細については、こちらの記事を参照してください。
OAuth 2.0イントロスペクションリクエストの処理
/api/{serviceId}/auth/introspection/standard
サービスのイントロスペクションエンドポイントの実装のために呼び出されるAPI このエンドポイントは第三者が管理するリソースサーバーがトークンの状態を検証するための標準的な方法をサポートしています。標準イントロスペクションエンドポイントを使用するリソースサーバーを制御することが困難なため、短時間内に同じトークンを複数回検証しようとする可能性があります。このシナリオにおいてはキャッシングが有効な場合があります。

アプリケーションにキャッシュを実装する際は、以下の点に注意してください。

  • クエリレスポンスのキャッシュ: 冪等な(安全かつ繰り返し可能な)エンドポイントからの成功レスポンスを主に一定期間において保存するように実装してください。インメモリーキャッシュまたは内部キャッシュシステム(例:Redis)を使用することによって、繰り返しのクエリを処理し、Authlete APIへの負荷を大幅に軽減させることができます。
  • 設定の確認: 高トラフィック時のリソース枯渇や連鎖的な障害を防ぐために、最大接続数の適切な設定なども含め、トラフィックパターンを処理できるように、キャッシングインフラが十分堅牢に設定されていることを確認してください。
  • 有効期限(TTL)の管理: データの変動性に基づいた適切なTTLを定義するように実装してください。重要度の低いデータや静的データは、より長いTTL(例:5〜10分)に設定しても問題ありません。

条件付きリトライロジックの導入

AuthleteのAPIを使用するアプリケーションは、一時的な失敗のみを対象としたリトライロジックを実装し、すべてのリクエストがシステムに処理される状態にする必要があります。すべてのリクエストに対して即座に再実行するアプリケーションは、複合的にエラーを引き起こし、システムリソースを圧迫し、接続枯渇やタイムアウト、さらには自らDDoS状態を招いてしまうリスクがあります。

アプリケーションに対するAuthleteのリトライロジックを実装する際は、以下の点に注意してください。

  • 一時的なエラーのみのリトライの実行: 自動リトライは、一時的なサーバー問題を示したHTTPステータスコードのみに対して使用してください。
    • 429 Too Many Requests: レート制限を超過したことを示すコード。リトライはRatelimit-Resetヘッダーに指定された時間が経過した後にのみ行ってください。
    • 503 Service Unavailable: Authlete APIが一時的に過負荷状態またはメンテナンス中であることを示すコード。
    • 502 Bad Gateway Error: Authlete APIがメンテナンスのためにダウンしていることを示すコード。
    • その他の 5xx エラー(例:500): エラーが永続的でない、かつ発生しているエンドポイント固有のものである可能性がある場合にのみリトライしてください。
  • 永続的なエラーのリトライの回避: 400 Bad Request401 Unauthorized403 Forbidden などの永続的なエラー(4xx)に対してはリトライを実装しないでください。リクエストの内容を修正しない限り成功することはありません。
  • リトライの重複の回避: アプリケーションがリトライを追跡し、同じリクエストコンテキストにおいて、すでにリトライ済みの操作を再度リトライしないように実装してください。これにより、不要なリトライの連鎖を防ぐことができます。

指数バックオフによる遅延の導入

レート制限違反をさらに悪化させてしまうリクエストの急激な増加(「リトライストーム」)を防ぐために、リトライ間に制御された遅延を導入してください。

  • 指数バックオフの使用: 失敗が連続している際に遅延が指数的に増加する指数バックオフアルゴリズムを実装してください。この仕組みによりリクエストレートが徐々に低下し、他のリクエストを処理するための時間をAuthlete APIに確保させられます。遅延は1回目に500ミリ秒、2回目に1秒、3回目に2秒というように待機し、また同時リトライを防ぐために細かいランダムジッターを加えることを推奨します。
  • 最大リトライ回数とジッターの定義: リトライの最大試行回数またはリトライに費やす最大時間を設定し、また遅延計算にランダムな「ジッター」係数を組み込んでください。ジッターにより、障害が発生したクライアントが全て同じタイミングでリトライしてしまうことを防ぎ、トラフィックスパイクが再発してしまうリスクを軽減させることができます。

サーキットブレーカーパターンの実装

指数バックオフと条件付きリトライを導入しても、持続的または一時的エラーが多発した場合にアプリケーションに対して重大なサービス障害を発生させてしまう可能性があります。この状況においては、失敗した操作を継続的に試行すると、アプリケーションリソースが枯渇し、Authlete APIへの負荷がさらに高まり、自ら障害を引き起こす結果につながりかねません。

アプリケーションに対してサーキットブレーカーパターンを実装する際は、以下の点に注意してください。

  • 失敗閾値の定義: 指定された短期間内において一時的なエラー(例:429 Too Many Requests、503 Service Unavailable)の発生率または件数が定義された閾値を超えた場合に、サーキットブレーカーが「オープン」(リクエストのルーティングを停止する)に変更するように設定してください。
  • 3つの状態(クローズ、オープン、ハーフオープン)の実装:
    • クローズ(Closed): 通常動作。エラー数を監視します。
    • オープン(Open): バックエンドを呼び出さずにリクエストを即座に失敗させます。ハーフオープンに変更する前に固定タイムアウト(例:60秒)を使用してください。
    • ハーフオープン(Half-Open): バックエンドが回復したかどうかを確認するために、限られた数のテストリクエストを通過させます。テストリクエストが成功した場合はクローズ状態に戻り、失敗した場合はオープン状態に戻ります。
  • ファストフェイルフォールバックの使用: ブレーカーがオープンの場合、アプリケーションや障害中のサービスに負担をかけることなく、機能が低下した状態でもユーザーエクスペリエンスを維持できるよう、即時かつリトライ不可のフォールバックレスポンス(例:キャッシュを使用できるエンドポイントに対して、キャッシュ済みのデータを提供するなど)を返すように実装してください。
  • リソースの分離: 障害を隔離できるよう、サーキットブレーカーを細かいレベル(例:エンドポイントごとなど)で導入してください。これにより、アプリケーションにとって最重要であるフロー(例:イントロスペクションやリフレッシュトークン交換など)を優先しながら、その他のフロー(例:クライアント管理)へのアクセスを制限することができます。

この仕組みは、不具合発生中のサービスに対する操作を迅速に停止することによって、アプリケーションの安定性を維持し、連鎖的な障害からシステム全体を保護する最終的な防御層です。