32.2.5 Kvóta-visszaállítás időzítésén alapuló támadások

2025.10.06.
AI Biztonság Blog

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.

Kapcsolati űrlap

AI Biztonság kérdésed van? Itt elérsz minket:

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:

  1. Időablak vége: A perc utolsó másodperceiben (pl. xx:58 és xx:59 között) elküld 100 kérést. Ezzel teljesen kimeríti az aktuális perc kvótáját.
  2. 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.

Fix Ablakos Kvóta Kihasználása 1. perc (Kvóta: 100) 2. perc (Kvóta: 100) Kvóta nullázás T-60s T T+60s 1. Csomag (100 kérés) 2. Csomag (100 kérés) Eredmény: ~200 kérés 2-3 másodperc alatt

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-Reset vagy Retry-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 Requests hibá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.
Védekezés: A csúszóablakos mechanizmus

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.