
レート制限をPythonで実装する|総当たり攻撃を"遅く"して止める
2026-09-16 ・ 実践
パスワードの総当たり、APIの乱用、スクレイピング——これらを止める基本の防御が レート制限(rate limiting) です。「一定時間に何回まで」を制限するだけで、攻撃の多くが割に合わなくなります。定番のトークンバケット方式をPythonで実装します。
発想:バケツにトークンを貯める
「一定ペースでトークンが補充されるバケツ」を用意し、リクエストのたびに1枚使う。空なら拒否。バーストは許しつつ平均レートを抑えられる、シンプルで強力な方式です。
トークンバケット
バケツ
上限まで貯まる
一定ペース補充
毎秒N枚
1回=1枚消費
空なら拒否
準備
標準ライブラリのtimeだけ。
① トークンバケットを実装
import time
class RateLimiter:
def __init__(self, rate, capacity):
self.rate = rate # 1秒あたり補充されるトークン数
self.capacity = capacity # バケツの最大容量
self.tokens = capacity
self.last = time.monotonic()
def allow(self):
now = time.monotonic()
# 経過時間ぶんトークンを補充(上限まで)
self.tokens = min(self.capacity, self.tokens + (now - self.last) * self.rate)
self.last = now
if self.tokens >= 1:
self.tokens -= 1
return True
return False
② 総当たりを止めてみる
「1秒に2回まで」の制限で、連続10回のログイン試行を通します。
limiter = RateLimiter(rate=2, capacity=2) # 毎秒2回・バースト2まで
allowed = 0
for i in range(10):
if limiter.allow():
allowed += 1
time.sleep(0.1) # 0.1秒間隔で連打(=毎秒10回ペース)
print(f"10回中 通過: {allowed} 回、拒否: {10 - allowed} 回")
毎秒10回のペースで叩いても、補充が毎秒2枚なので大半が拒否されます。攻撃者が1万通り試すのに現実的でない時間がかかるようになる——これがレート制限の効果です。
キー単位で持つ
実務では「IPアドレス単位」「ユーザーID単位」でバケツを分けて管理します(辞書に RateLimiter を持つイメージ)。分散環境ではRedisなどで共有カウンタにするのが定番です。
レート制限だけに頼らない
レート制限は総当たりを"遅く"しますが、単体で万能ではありません。強いパスワードハッシュ、多要素認証、アカウントロック、CAPTCHA と組み合わせて多層で守ります。またIP偽装や分散攻撃には別の対策が要ります。
まとめ
- レート制限は「一定時間に何回まで」を制限する基本防御
- トークンバケット方式はバーストを許しつつ平均レートを抑える
- 総当たりを非現実的な時間に引き伸ばして割に合わなくする
- IP/ユーザー単位で管理し、他の対策と多層で組み合わせる
もう少し詳しく(背景と理論)
レート制限は、単位時間あたりのリクエスト数に上限を設けて、総当たり攻撃・スクレイピング・DoS・APIの濫用を防ぐ基本的な防御です1。代表的なアルゴリズムが トークンバケット(一定速度でトークンを補充し、リクエストごとに消費。バースト許容)と リーキーバケット、そしてスライディングウィンドウです2。分散環境では各サーバーがバラバラに数えると制限が緩むため、Redis 等の共有ストアでカウンタを一元管理するのが定石です3。認証エンドポイントでは特に厳しめに設定し、超過時は HTTP 429(Too Many Requests)と Retry-After ヘッダを返すのが標準的な作法です。
次の一歩 🌸
パスワード保存のハッシュ実装、2つ目の要素のTOTP実装、総当たりの土台知識はパスワードの基礎へどうぞ。