一覧SP-23-1 · DDoS・Bot・API保護:要求の分布と仕様

レート制限の方式

集計単位と時間条件を具体化し、制限後に通る要求と資源負荷を説明します。

説明

DDoS対策や頻度制限では、回線容量、接続数、要求数、アプリの処理負荷など異なる資源を扱います。要求をどの主体と時間幅で数えるかによって、通常利用者への影響も変わります。

6件の固定要求列へ、全体、送信元IP、利用者単位の制限を適用します。固定窓の境界と移動窓を比較し、同じ要求数でも処理量が違う例を集計します。

集計単位と時間条件を具体化し、制限後に通る要求と資源負荷を説明します。

1要求列と処理コストを読む

各行はID、秒、IP、利用者、固定の処理コストです。

from sp23 import * for row in EVENTS要求列と処理コストを読む:print(row)

aliceとbobは同じIP、carolは別IPです。共有回線ではIPを一人の利用者とみなせません。

2全体を10秒で2件に制限する

固定窓は0〜9秒、10〜19秒という区切りです。

print(fixed全体を10秒で2件に制限する(EVENTS,key=None))

各窓の3件目は429です。全体上限では別の利用者の要求も同じ枠を消費します。返り値の後半は窓番号と、その窓の試行数です。

3IP単位で数える場合を比較する

同じIPに2件ずつの枠を設けます。

ip_result=fixedIP単位で数える場合を比較する(EVENTS,key=2) print(ip_result)

r3のbobはaliceが使った枠の影響を受けます。IP単位の制限は、共有IPの正常利用者にも作用します。

4利用者単位へ集計を変える

利用者IDで数えます。

print(fixed利用者単位へ集計を変える(EVENTS,key=3))

この列では全件200です。利用者IDは検証済みの主体から取得する必要があり、自由に付けられるヘッダーをそのままキーにはしません。

5固定窓の境界に要求が集中する場合を見る

9秒に2件、10秒に2件を置きます。

burst=[(str(i),t,'192.0.2.7','alice',1) for i,t in enumerate([9,9,10,10],1)] print(fixed固定窓の境界に要求が集中する場合を見る(burst))

1秒を挟んで4件とも通ります。固定窓の「10秒に2件」は、任意の連続する10秒に2件という条件とは異なります。

6移動窓の制限と比較する

直前10秒で許可した件数を数える別モデルです。

print(sliding移動窓の制限と比較する([9,9,10,10])) print(sliding([0,9,10]))

最初の列は後半2件が429です。2つ目では10秒時点で0秒の要求が範囲外になり許可されます。拒否試行を数えるかもアルゴリズムの条件に含めます。

7許可した要求の処理量を求める

要求数とコスト合計を並べます。

allowed={ident for ident,status,bucket,count in ip_result if status==200} print(len(allowed),sum許可した要求の処理量を求める(cost for ident,t,ip,user,cost in EVENTS if ident in allowed)) print('all requests',len(EVENTS),sum(row[4] for row in EVENTS))

IP制限後は5件・コスト8、全体は6件・コスト12です。件数だけの閾値では高コストな処理を同じ重さで扱います。上流で回線が飽和する場合はアプリの429だけでは解決できず、制御地点も見直します。

まとめ

回線・接続・要求・処理コストの尺度を分けます。

IPや利用者など集計キーの影響を確認します。

固定窓の境界と移動窓の条件を照合します。

操作履歴と解説を振り返れます。

Python 3.15 / 固定要求列・HMAC・JSON Schema 2020-12

1
from sp23 import * for row in EVENTS:print(row)
('r1', 8, '192.0.2.7', 'alice', 1)
('r2', 9, '192.0.2.7', 'alice', 1)
('r3', 9, '192.0.2.7', 'bob', 4)
('r4', 10, '192.0.2.7', 'bob', 4)
('r5', 10, '192.0.2.8', 'carol', 1)
('r6', 11, '192.0.2.7', 'alice', 1)
2
print(fixed(EVENTS,key=None))
[('r1', 200, 0, 1), ('r2', 200, 0, 2), ('r3', 429, 0, 3), ('r4', 200, 1, 1), ('r5', 200, 1, 2), ('r6', 429, 1, 3)]
3
ip_result=fixed(EVENTS,key=2) print(ip_result)
[('r1', 200, 0, 1), ('r2', 200, 0, 2), ('r3', 429, 0, 3), ('r4', 200, 1, 1), ('r5', 200, 1, 1), ('r6', 200, 1, 2)]
4
print(fixed(EVENTS,key=3))
[('r1', 200, 0, 1), ('r2', 200, 0, 2), ('r3', 200, 0, 1), ('r4', 200, 1, 1), ('r5', 200, 1, 1), ('r6', 200, 1, 1)]
5
burst=[(str(i),t,'192.0.2.7','alice',1) for i,t in enumerate([9,9,10,10],1)] print(fixed(burst))
[('1', 200, 0, 1), ('2', 200, 0, 2), ('3', 200, 1, 1), ('4', 200, 1, 2)]
6
print(sliding([9,9,10,10])) print(sliding([0,9,10]))
[200, 200, 429, 429]
[200, 200, 200]
7
allowed={ident for ident,status,bucket,count in ip_result if status==200} print(len(allowed),sum(cost for ident,t,ip,user,cost in EVENTS if ident in allowed)) print('all requests',len(EVENTS),sum(row[4] for row in EVENTS))
5 8
all requests 6 12

演習完了