総当たり対策のレート制限は失敗だけ数え、成功でリセットする — 成功も数えると正規利用者が締め出される
セキュリティ
設計判断
API設計
判断
運用
認証の総当たり対策としてレート制限を置くとき、判定と加算の設計を分けて考える必要がある。素直にミドルウェアの入口で無条件に加算すると、成功したログインも1回消費するため、同じ利用者が短時間に複数回ログインしただけで締め出される。トークン期限切れ・複数端末・再認証のある構成では現実的に起こる。
判断基準
- 総当たり対策の制限は「失敗した試行」だけを数え、成功したらカウンターを削除(リセット)する。守りたいのは「当たるまで繰り返す」行為であって、正規の利用回数ではない。
- 一方、登録スパム対策のような「作成そのものを抑えたい」制限は、成功も含めて全試行を数えるのが正しい。同じ仕組みに見えても数え方の正解が逆になるため、目的をエンドポイントごとに言語化してから実装する。
- この差は将来の実装者が「片方に合わせて統一」しがちなので、意図的な差であることをテストとして固定する(成功応答でもカウントが増える/増えないを別々に検証する)。
落とし穴
- 失敗のみ数えるには、ハンドラーの実行後に結果を見る必要がある。素直に「後続処理を呼んだ直後に加算」と書くと、下流が異常終了して制御が戻らない経路で加算が飛ばされる。異常終了を誘発するリクエストは何度でも通過でき、制限そのものを回避できる。加算は必ず遅延実行(スコープ離脱時に必ず走る仕組み)で行い、異常終了も失敗として数える。
- 判定と加算が分離するため、境界付近の同時実行で上限を数回超えうる。小さな上限では誤差として許容できるが、厳密性が要るなら判定と加算を原子化する別設計になる。どちらを選んだか記録する。
検証方法
- 失敗を上限まで繰り返した後、次が拒否されること。
- 失敗を上限手前まで積んだ後に成功させ、その後また通ること(リセットの確認)。
- 成功応答が加算されないこと。
- 異常終了するハンドラーへの連続試行が、最終的に拒否されること。この1本が抜けると回避経路が残る。