レート制限の方式
集計単位と時間条件を具体化し、制限後に通る要求と資源負荷を説明します。
説明
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
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)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)]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)]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)]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)]print(sliding([9,9,10,10]))
print(sliding([0,9,10]))[200, 200, 429, 429] [200, 200, 200]
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
演習完了