3

私はGoogleAppEngineプロジェクト(python / webapp2)に取り組んでおり、大量のリクエストで作成しているサービスを悪用/スパムする人々に少し懸念を抱いています。この可能性に対抗するために、私の考えは、アプリケーションの特定の部分について、特定の時間にIPアドレスごとに許可される要求の数を制限することです。私の現在の計画は次のとおりです。

リクエストごとに、次のことを行います。

  1. ヘッダーからIPアドレスを取得します
  2. このIPアドレスをタイムスタンプ付きでデータストアに保存します
  3. 1時間以上経過しているIPアドレスエンティティを削除します
  4. そのIPアドレスを持つデータストアエンティティの数を数える
  5. 制限を超えている場合はアクセスを禁止する

私の質問はこれです:
これはこれについて行くための最良の方法ですか?私はここでは初心者であり、この方法でそれを行うにはかなりのオーバーヘッドがあり、おそらくこれはより良い解決策があるかもしれない一般的なタスクであると思います。リソースをあまり消費しない、これを行うためのより良い方法はありますか?

4

2 に答える 2

11

過去に、私はmemcacheを使用してこれを実行しました。これは、特におおよその制限のみを気にするため、はるかに高速です(memcacheはシステムによってフラッシュされる可能性があるため、すべてのインスタンスで共有されるとは限らないなど)。キーを期限切れにするために使用することもできます。このようなもの(selfwebapp2リクエストハンドラーであり、GAEのmemcacheライブラリをインポートしたことを前提としています):

memcache_key = 'request-count-' + self.request.remote_addr

count = memcache.get(memcache_key)

if count is not None and count > MAX_REQUESTS:
    logging.warning("Remote user has %d requests; rejecting." % (count))
    self.error(429)
    return

count = memcache.incr(memcache_key)
if count is None:
    # key didn't exist yet
    memcache.add(memcache_key, 1, time=WINDOW_IN_SECONDS)

これにより、WINDOW_IN_SECONDS時間の約MAX_REQUESTS後にユーザーを拒否するキーが作成され、WINDOW_IN_SECONDSごとにカウントが再ゼロ化されます。(つまり、スライディングウィンドウではなく、期間ごとにゼロにリセットされます。)

于 2013-01-17T01:41:09.733 に答える
2

まず、設計に関する2つの注意事項:

  • 多くの場合、誰かが新しいIPアドレスを取得するのは非常に簡単です。iPhoneをLTEから3Gに切り替えて元に戻し、DSLモデルのプラグを抜き差しし、新しいオープンプロキシを選択するなどです。したがって、意図的な悪用を防ぐためにこれを期待している場合人々が自分たちがやりすぎていることに気づかないだけでなく、それはあまり役に立ちません。

  • IPアドレスは、多くの場合、NATによって、または順次に共有されます。1人の人を意味する場合はIPあたり1時間あたり200リクエストが妥当と思われるかもしれませんが、BigCorpの地域オフィスの7500人の従業員全員を意味する場合はどうでしょうか。

とにかく、あなたの解決策はうまくいくでしょう、そしてあなたのトラフィックパターンに応じて、それは合理的かもしれません、しかしいくつかの選択肢があります。

たとえば、すべての接続をチェックする代わりに、共有ブラックリストを保持したい場合があります。接続が確立されたら、そのブラックリストに基づいてすぐに承認または拒否し、「データベースの更新」ジョブを開始します。N秒ごとに1回以上更新するのではなく、更新を統合するためのさらなるトリックを実行できます。もちろん、これは、すべての接続で読み取り可能で、バックグラウンドジョブで書き込み可能な共有データがあることを意味します。つまり、競合状態やデッドロックへの扉、そしてGuidoがGAEに直面することはめったにないことを確認するために懸命に努力したすべての楽しいこと。

dataStoreの代わりにmemcacheを使用できます。ただし、キーを慎重に作り直して、単純なKey-Valueストアに適したものにし、有効期限が必要な処理を実行できるようにする必要があります。たとえば、IPからキーオフされた値に加えて、タイムスタンプや乱数など、接続ごとにキーを設定した値に加えて、他の値を見つけることができるIPからキーオフされた接続リストの値を保持できます。キャッシュからドロップされた値はカウントされなくなり、接続リストの値がドロップされた場合、ユーザーは0まで下がる必要があります。ただし、これにより多くの複雑さが増します。

少数のユーザーがそれぞれ大量のリクエストを行っている場合は、タイマーを使用して、IPごとにデクリメント、リセット、または再カウントすることができます。ただし、1時間あたり数百を超える個別のIPが予想される場合は、これらすべてのタイマーを手動で合体する必要があり、おそらくジョブも合体する必要があります(たとえば、「17:55:39に、この17のIPのリストをデクリメントする」)。 、そしてタイマーはおそらく非常に頻繁に起動するので、おそらくそれだけの価値はありません。

個人的には、最初に最も単純な実装を行い、次にストレステストとパフォーマンステストを行います。それで十分であれば、心配する必要はありません。

And if it's not good enough, I might look into whether I could simplify the design before looking at optimizing the implementation. For example, if it's N connections per IP per calendar-hour, that makes everything a whole lot easier—just store a counter per IP (in dataStore or memcache), and wipe all the counters at every XX:00. Is that acceptable?

于 2013-01-17T01:54:57.993 に答える