概要:
私の会社はレート制限APIを開発しました。私たちの目標は2つあります。
明確化:なぜレート制限が必要なのですか?
APIは製品への追加として販売しているため、APIのレート制限を行っています。 APIへの匿名アクセスでは、1時間あたりのAPI呼び出しのしきい値が非常に低くなっていますが、有料のお客様には1時間あたり1,000回以上の呼び出しが許可されています。
問題:
レート制限されたAPIは、開発者のエコシステムには最適ですが、ドッグフードを提供するために、同じレート制限に制限することはできません。 APIのフロントエンドはすべてJavaScriptであり、APIに直接Ajax呼び出しを行います。
質問は次のとおりです。
レート制限を削除するプロセスで簡単にスプーフィングできない場合に、レート制限を削除できるようにAPIを保護するにはどうすればよいですか?
探索された解決策(およびなぜ機能しなかったのか)
ホストヘッダーに対してリファラーを確認します。 -referrerは簡単に偽造されているため、欠陥がある。
[〜#〜] hmac [〜#〜] を使用して、リクエストと共有シークレットに基づいて署名を作成し、サーバーでリクエストを検証します。 -フロントエンドJavaScriptを調べることで秘密とアルゴリズムが簡単に決定されるため、欠陥があります。
リクエストをプロキシし、プロキシでリクエストに署名します-プロキシ自体がAPIを公開するため、まだ欠陥があります。
質問:
Stack Overflowの素晴らしい頭脳に注目して、代替ソリューションを紹介します。この問題をどのように解決しますか?
独自のJavaScriptクライアントがAPIに直接アクセスしているため、誰でも同じAPIキーを使用するなど、何をしているのかを見て模倣することができます。コードを難読化したり、さまざまなハードルを設定したりするなどして、さらに難しくすることもできますが、あなたと抑制しようとしている人は基本的に同じアクセス権を持っています。権限の違いを作成しようとする代わりに、非公式クライアントがそのスコープ内のすべてのアクセスを使用してもまったく問題ないシステムを構築する必要がありますが、システムは、すべてのクライアントで公式に使用されるように配置されます大きい。
これは、多くの場合、アプリケーション全体に対して1つのトークンではなく、ユーザーごとのアクセストークンを使用して行われます。各トークンの制限は、APIの通常の使用には十分ですが、悪用しようとする人には制限が必要です。たとえば、通常のブラウジングをサポートするには1分間に100回の呼び出しで十分かもしれませんが、あなたをこすり落としたい場合、その予算で効果的にそれを行うことはできません。
常に軍拡競争があります-たくさんのボットユーザーアカウントを作成することで制限を回避できます。ただし、実際の人間にわずかな費用をかけて、サインアップフローにキャプチャを追加するだけでは、かなり解決された問題になります。これらのシナリオに入ると、すべては利便性と制限のトレードオフにすぎません。完全に防弾の何かを見つけることは決してないので、それを十分に良くすることに集中し、誰かがあなたを利用して穴がどこにあるかを知るまで待ちます。
これが問題を引き起こしている場合、開発者の推定エコシステムに問題を引き起こします(たとえば、代替UIを開発しようとするとき)。実際に自分のドッグフードを食べている場合は、アプリケーションでAPI(およびレート制限)が機能するようにします。以下にいくつかの提案を示します。
IPアドレスでレート制限しないでください。むしろ、ユーザーに関連付けられた何かによるレート制限。ユーザーID。認証段階でレート制限を適用します。
ユーザーが継続的に呼び出す必要がないようにAPIを設計します(たとえば、毎回1つのアイテムを返す繰り返し呼び出しではなく、多くの結果を返すリスト呼び出しを行います)
開発者のエコシステムに期待するのと同じ制約でWebアプリを設計します。つまり、妥当な調整率内で設計できるようにします。
バックエンドがスケーラブルであることを確認し(水平方向が望ましい)、UIに実際に問題を引き起こすほど低いレベルで調整する必要がないようにします。
スロットルがバーストに対処する能力を持ち、長期的な悪用を制限するようにします。
スロットルが、削除しようとしている虐待に合わせた賢明なアクションを実行するようにします。たとえば、接続を拒否するのではなく、軽度の虐待者をキューに入れるか遅らせることを検討してください。ほとんどのWebフロントエンドは、同時に4つの同時接続のみを開きます。 5番目を開く試みを遅らせると、Webクライアント(2つのWebクライアント)と同時にCLIを使用している場合にのみヒットします。 n番目のAPI呼び出しを失敗するのではなく、ギャップなしで遅延させると、エンドユーザーには問題が発生するのではなく、速度が低下します。これを一度にN API呼び出しのみをキューに入れると組み合わせると、多数のAPI呼び出しを並列化しているユーザーのみにヒットします。これはおそらく、望んでいない動作です。 100の同時API呼び出しは、1時間のギャップは通常、1時間の100の順次API呼び出しよりもはるかに悪いです。
これはあなたの質問に答えませんでしたか?まあ、あなたが本当にneedあなたが求めていることをするなら、認証段階でレート制限を行い、ユーザーが適合するグループに基づいて異なるレート制限を適用します。 1つの資格情報セット(開発者とQAチームが使用)を使用している場合、より高いレート制限が適用されます。しかし、開発者とQAチームには見られない問題を見て、どうしてこれがエコシステムに必然的につながるのか、すぐにわかります。
製品を購入します。自分の有料顧客になります。
「APIへの匿名アクセスでは、1時間あたりのAPI呼び出しのしきい値が非常に低くなっていますが、有料のお客様には1時間あたり1000回以上の呼び出しが許可されています。」
これは、顧客の観点からシステムをテストするのにも役立ちます。
残念ながら、これに対する完璧な解決策はありません。
一般的なアプローチは、通常、クライアントが自分自身を識別するためのspoofable方法(たとえば、識別子、バージョン、およびAPIキー)を提供することです。アクセスを制限します(たとえば、クライアントは特定のIPアドレス範囲のサーバーであるため、その範囲の呼び出し元のみを許可します。たとえば、クライアントはJavaScriptですが、特定のカテゴリーのブラウザーにのみ配信されるため、特定のHTTP要求へのアクセスのみを許可しますユーザーエージェント文字列など)、機械学習/パターン認識を使用して、なりすましクライアントである可能性のある異常な使用を検出し、これらのなりすましクライアントからのトラフィックを拒否します(または、これらの使用が本当に正当なクライアント、なりすまし可能な資格情報を置き換えてから、古いなりすまし資格情報を使用したそれ以上のトラフィックを禁止します)。
キーの複数のレイヤーを使用することで、スプーフィングを少し難しくすることができます。たとえば、サーバー上に存在する(そして限られたIPアドレス範囲でのみ使用できる)より長期間有効な資格情報を提供して、クライアント(ユーザーエージェントなど)に関する情報を記録するAPI呼び出しを行います。クライアント側のAPIリクエストのためにクライアントで使用するためにJavaScriptでシンジケートされる、短命のクライアント側キーを返します。これも不完全です(スプーファーは同じサーバー呼び出しを発行して資格情報を取得できます)が、返されたAPIキーが難読化された(そして頻繁に変更される)JavaScriptまたはHTMLに含まれていると、より困難になります応答から確実に抽出します)。また、スプーフィングをより簡単に検出する方法も提供されます。現在、クライアント側のキーは特定のクライアント(たとえば、特定のユーザーエージェント、おそらく特定のcookie jar)に関連付けられており、別のクライアントでの再利用が検出しやすくなり、失効により、スプーフィングされたキーが再利用される期間も制限されます。
問題のアプリは一般公開されている必要があると仮定すると、あまり選択肢はありません。
APIの威力を示す別の方法を選択してください。たとえば、そのようなアプリを作成してソースを共有しますが、実際にはそのコードを実行しないでください。ただし、ドキュメントが適切に文書化されていることを確認してください。そうすれば、誰でも展開して動作することを確認できます(調整の対象)。
実行するアプリは、クライアント側のAPIリクエストを回避し、よりサーバーレンダリングされるようにリファクタリングする必要があります。 APIをドッグフードすることはできますが、明らかな方法ではありません。サーバー側からスロットルなしのAPIへの安全なリクエストを作成します。
レート制限の調整は、アプリが動作し、負荷を処理するためのパフォーマンスの最適化に投資できるようにします。
ええ、そもそもコアAPIをスロットルなしで使用し、プライベートネットワーク内に保管してください。公的にアクセス可能な別のレイヤーでスロットルします。
特定のIPアドレス/ユーザーにバインドされ、有効期間が制限された一意のセッションIDの生成を試みることができます。ユーザーがアプリケーションのフロントエンドJavaScriptコードをダウンロードすると、生成されたセッションIDがJavaScriptソースコードに挿入されます。セッションIDはAPIへのすべてのリクエストに添付され、レート制限が解除されます。
IDは、単一のIPアドレス、ユーザー、および限られた時間に対してのみ有効であるため、なりすましのために単純にコピーすることはできません。そのため、攻撃者はページを呼び出して、JavaScriptソースからキーをフィルタリングするか、新しいユーザーが使用するたびにAjaxリクエストをインターセプトする必要があります。
別のオプション:
独自のアプリケーションのプロキシを設定し、難読化を使用します。プロキシへのAjaxリクエストは、実際のAPI呼び出しとは異なる名前を使用し、プロキシはそれらを変換して戻します。したがって、アプリケーションは実際のAPIでgetDocument
を呼び出しませんが、プロキシでgetFELSUFDSKJE
を呼び出します。プロキシはこのコールをgetDocumentに変換して戻し、実際のレート制限APIに転送します。
実際のAPIは、プロキシによるリクエストのレート制限を行いません。
他の人が自分のアプリケーションにプロキシを使用しないように、難読化スキームを毎日変更します。難読化された呼び出し名は、JavaScriptソースコードで自動的に生成され、プロキシで構成できます。
これを使用したいクライアントも、プロキシを使用するために、難読化の変化に対応する必要があります。また、リファラーヘッダーなどをログに使用することもできるため、プロキシを使用しているユーザーを見つけることができます。または、難読化スキームを変更するときにそれらをキャッチします。
UIとスロットルなしのAPIの個別のインスタンスを立ち上げ、組織からのIPアドレスへのアクセスを制限できますか?
たとえば、企業のファイアウォールの背後にすべてを展開し、インスタンス間でデータを共有する必要がある場合は、公開されているインスタンスと同じデータベースにアプリケーションを接続します。
複数のアカウントを設定し、リクエストごとにランダムに1つを選択するか、使用するアカウントを1時間ごとに変更します。このようにすると、負荷をn
アカウントに分散し、最大n
倍の上限を設定できます。
顧客に許可されていない場合、これを行う他のユーザーを見つけようとする場合、誤ってシャットダウンすることに注意してください。