A rate limit mechanizmusok nem csupán egy numerikus korlátot jelentenek; egy belső óra ketyeg bennük. Ha ez az óra egy fix, előre meghatározott időpontban (pl. minden perc elején) nullázza a számlálót, akkor egy kritikus, bár rövid ideig tartó sebezhetőségi ablak nyílik meg. Ezt az ablakot használjuk ki a kvóta-visszaállítási támadások során, ahol a védelem merevségét fordítjuk önmaga ellen.
A sebezhetőség anatómiája: A dupla kvóta illúziója
A támadás alapja a fix ablakos (fixed-window) kvótaszámlálók működése. Ezek a rendszerek egy adott időintervallumra (pl. egy percre) engedélyeznek N számú kérést. Amikor az intervallum lejár, a számláló azonnal nullázódik, és egy új, tiszta N-es keret áll rendelkezésre. A támadó célja, hogy pontosan a nullázás pillanatában hajtson végre egy kéréscsomagot.
Képzelj el egy rendszert, ami percenként 100 kérést engedélyez, és a számláló minden perc 00. másodpercében nullázódik. A támadó stratégiája:
- Időablak vége: A perc utolsó másodperceiben (pl.
xx:58ésxx:59között) elküld 100 kérést. Ezzel teljesen kimeríti az aktuális perc kvótáját. - Időablak eleje: Amint az óra átfordul a következő percre (
xy:00), azonnal elküld egy újabb 100 kéréses csomagot.
Az eredmény? A támadó néhány másodpercen belül sikeresen végrehajtott 200 kérést egy olyan rendszeren, amit percenként 100 kérésre terveztek. Ez a hirtelen terhelésnövekedés elegendő lehet a rendszer ideiglenes megbénítására, vagy egy gyors adatkinyerési művelet végrehajtására.
Gyakorlati végrehajtás
A támadás sikere a precíz időzítésen múlik. Ez két fő lépésből áll: a visszaállítási időpont felderítéséből és a szinkronizált kérések indításából.
1. Lépés: A Visszaállítási Időpont Felderítése
Mielőtt a támadást elindítanánk, pontosan ismernünk kell a nullázás időpontját. Ezt többféleképpen kideríthetjük:
- HTTP Válaszfejlécek: Sok API visszaküldi a kvóta állapotát fejlécekben, mint például
X-RateLimit-ResetvagyRetry-After. Az itt található időbélyeg (timestamp) vagy másodpercben megadott érték elárulja a következő nullázás időpontját. - Aktív próbálkozás: Ha nincsenek informatív fejlécek, aktív módszerhez kell folyamodnunk. Folyamatosan küldünk kéréseket, amíg
429 Too Many Requestshibát nem kapunk. Ekkor várunk, és másodpercenként egy-egy kérést küldve figyeljük, mikor kapunk újra sikeres (200 OK) választ. Az első sikeres válasz időpontja jelöli ki a visszaállítási ciklus kezdetét.
2. Lépés: A Támadó Szkript Logikája
A felderített időpont birtokában egy egyszerű szkripttel automatizálhatjuk a támadást. A cél, hogy a helyi óránkat a szerver idejéhez szinkronizáljuk, majd a megfelelő pillanatban indítsuk a kéréscsomagokat.
# Pszeudokód a kvóta-visszaállítási támadásra
import time
import threading
# A szerver API végpontja
API_URL = "https://api.pelda.com/v1/muvelet"
# Percenkénti kvóta
RATE_LIMIT = 100
# A felderített visszaállítási időpont (pl. minden perc 00. másodperce)
RESET_SECOND = 0
def send_request():
# Itt történne a tényleges API hívás
print(f"Kérés elküldve: {time.time()}")
# ... requests.post(API_URL, ...)
def execute_burst(num_requests):
# Párhuzamosan küldjük a kéréseket a maximális sebességért
threads = []
for _ in range(num_requests):
thread = threading.Thread(target=send_request)
threads.append(thread)
thread.start()
for thread in threads:
thread.join()
while True:
current_time = time.localtime()
# Várunk, amíg a perc utolsó pár másodpercéhez nem érünk
if current_time.tm_sec > (60 - 5): # pl. az utolsó 5 másodpercben
print("\n--- Támadási ablak elérkezett ---")
# 1. Csomag: A kvóta kimerítése a nullázás előtt
print(f"Első csomag indítása {RATE_LIMIT} kéréssel...")
execute_burst(RATE_LIMIT)
# Pontos várakozás a nullázásig
while time.localtime().tm_sec != RESET_SECOND:
time.sleep(0.01) # Finomhangolt várakozás
print(f"\n--- KVÓTA VISSZAÁLLÍTVA ({time.ctime()}) ---")
# 2. Csomag: Az új kvóta azonnali kihasználása
print(f"Második csomag indítása {RATE_LIMIT} kéréssel...")
execute_burst(RATE_LIMIT)
print("\n--- Támadási ciklus vége, várakozás a következőre ---")
time.sleep(50) # Elkerüljük a felesleges CPU használatot
else:
time.sleep(0.5)
Hatás és védekezési stratégiák
Bár ez a támadás nem teszi lehetővé a korlátlan számú kérést, a rövid időre koncentrált dupla terhelés is komoly problémákat okozhat:
- Rövid távú szolgáltatáskiesés (DoS): A hirtelen terhelési csúcs túlterhelheti a háttérrendszereket, adatbázis-kapcsolatokat vagy akár magát az MI modellt.
- Biztonsági szűrők megkerülése: Ha egy másik biztonsági réteg (pl. WAF) a kérések gyakoriságát is figyeli, ez a technika átcsúszhat a radar alatt, mivel két különálló időablakban történik.
- Felgyorsított támadások: Prompt injection vagy adatlopási kísérleteknél a támadó kétszeres sebességgel tud próbálkozni, jelentősen csökkentve a sikeres támadáshoz szükséges időt.
A leghatékonyabb védekezés a fix ablakos számlálók elhagyása. Helyettük csúszóablakos (sliding-window) vagy token vödör (token bucket) algoritmusokat kell alkalmazni. A csúszóablakos módszer nem egy fix időponthoz kötött, hanem folyamatosan az utolsó N másodperc/perc kéréseinek számát összegzi. Így a támadó nem tudja kihasználni a „nullázási pontot”, mivel ilyen egyszerűen nem létezik. A rendszer folyamatosan, gördülékenyen követi a felhasználó aktivitását, megakadályozva a hirtelen, rövid idejű terhelési csúcsokat.