A felhőalapú és megosztott MI-szolgáltatások egyik alapvető ígérete az izoláció. Amikor egy API-hívást indítasz egy nagy nyelvi modell felé, joggal várod el, hogy a te kérésed és a másodpercekkel később egy teljesen másik ügyfél által indított kérés között hermetikus fal legyen. Azonban a fizikai hardver szintjén ez az izoláció gyakran inkább illúzió, mint kőbe vésett valóság. A megosztott erőforrások, különösen a CPU-gyorsítótárak, olyan rejtett csatornákat nyithatnak, amelyeken keresztül az egyik bérlő (tenant) akaratlanul is információt szivárogtathat ki egy másiknak.
Ez a jelenség, a bérlők közötti információszivárgás (cross-tenant information leakage), a gyorsítótár-időzítésen alapuló támadások egyik legveszélyesebb megnyilvánulása. Nem elméleti lehetőség; a Spectre és Meltdown sebezhetőségek óta tudjuk, hogy a modern processzorok spekulatív végrehajtása és komplex gyorsítótár-hierarchiái kihasználhatók. MI-kontextusban ez azt jelenti, hogy egy rosszindulatú felhasználó képes lehet következtetéseket levonni egy másik, ugyanazon a hardveren futó felhasználó interakcióiról.
A támadási felület: A megosztott erőforrások árnyoldala
A modern MI-infrastruktúrák a hatékonyság maximalizálására törekednek. Egyetlen nagy teljesítményű GPU-n vagy CPU-n egyszerre több felhasználó modellinferenciája is futhat. Bár a folyamatokat a hypervisor vagy a konténerizációs réteg logikailag elszigeteli, a fizikai erőforrásokon osztoznak. Ezek közül a legkritikusabb a gyorsítótár (L1, L2, L3 cache).
Amikor egy bérlő folyamata fut, betölti a szükséges adatokat és utasításokat a gyorsítótárba. Amikor a rendszer kontextust vált, és egy másik bérlő folyamata kerül sorra ugyanazon a CPU-magon, az előző bérlő „lábnyoma” ott maradhat a gyorsítótárban. Egy rosszindulatú bérlő (a támadó) ezt a lábnyomot elemezheti, hogy információt nyerjen ki az áldozat (egy másik bérlő) műveleteiről. A leggyakrabban használt technika erre a Prime+Probe.
A Prime+Probe támadás anatómiája
A Prime+Probe egy klasszikus gyorsítótár-mellékcsatorna támadás, amely három egyszerű, de hatékony lépésből áll. A támadó célja, hogy megfigyelje, az áldozat mely gyorsítótár-sorokat (cache lines) használja.
1. Fázis: Prime (Feltöltés)
A támadó egy ismert memóriaterülethez fér hozzá, ezzel szándékosan betölti a célzott gyorsítótár-sorokat (cache set) a saját adataival. Ezzel egy ismert, „tiszta” állapotot hoz létre.
2. Fázis: Áldozat futása
A támadó folyamata szünetel, és az operációs rendszer ütemezője az áldozat folyamatát futtatja ugyanazon a CPU-magon. Az áldozat műveletei során a saját adataihoz fér hozzá, amelyek közül néhány ugyanazokat a gyorsítótár-sorokat használja, mint amiket a támadó korábban feltöltött. Ez a folyamat „kilöki” (evicts) a támadó adatait a gyorsítótárból, helyet csinálva az áldozat adatainak.
3. Fázis: Probe (Szondázás)
Miután az áldozat futása befejeződött (vagy egy időszelet lejárt), a támadó folyamata újra aktívvá válik. A támadó ismét megpróbálja elérni az eredetileg a gyorsítótárba töltött adatait, és közben pontosan méri az egyes hozzáférések idejét.
- Gyors hozzáférés (cache hit): Ha az adat elérése gyors, az azt jelenti, hogy az adat még mindig a gyorsítótárban van. Az áldozat nem használta ezt a gyorsítótár-sort.
- Lassú hozzáférés (cache miss): Ha az adat elérése lassú, az azt jelenti, hogy a gyorsítótárból kilökték, és a lassabb főmemóriából (RAM) kellett visszatölteni. Ez egyértelmű jelzés arra, hogy az áldozat használta ezt a konkrét gyorsítótár-sort.
Az összegyűjtött „gyors” és „lassú” hozzáférések mintázata felfedi, hogy az áldozat mely memóriaterületekhez fért hozzá, ami már önmagában is értékes információ.
// Pszeudokód a Prime+Probe támadás logikájának bemutatására
function prime_and_probe_attack(cache_sets_to_monitor):
// 1. Fázis: Prime
for set in cache_sets_to_monitor:
fill_cache_set_with_my_data(set)
// Várakozás, hogy az OS az áldozatot ütemezze
yield_cpu()
// Az áldozat (victim) itt fut, és potenciálisan kilöki
// a mi adatainkat a gyorsítótárból.
// 3. Fázis: Probe
leaked_pattern = []
for set in cache_sets_to_monitor:
start_time = high_precision_timer()
access_my_data_in_set(set)
end_time = high_precision_timer()
access_duration = end_time - start_time
if access_duration > CACHE_MISS_THRESHOLD:
// Lassú elérés -> Cache miss
leaked_pattern.append(1) // Az áldozat használta ezt a sort
else:
// Gyors elérés -> Cache hit
leaked_pattern.append(0) // Az áldozat nem használta
return leaked_pattern
Konkrét MI-vonatkozások
Hogyan fordítható le ez a technika az MI-modellek világára? A memóriahozzáférési mintázatok egy modell esetében nem véletlenszerűek, hanem szorosan kötődnek a bemenethez és a belső működéshez.
- Bemeneti adatok szivárgása: Bizonyos tokenek vagy input-jellemzők feldolgozása a modell súlymátrixainak vagy a KV-gyorsítótárnak specifikus részeihez való hozzáférést igényli. Egy támadó, aki képes monitorozni ezeknek a memóriaterületeknek a gyorsítótár-használatát, következtethet a bemeneti prompt tartalmára. Például, ha egy orvosi diagnosztikai modellnél a „daganat” szó feldolgozása mindig egy bizonyos memóriacím-tartományhoz való hozzáférést vált ki, a támadó detektálhatja, ha az áldozat ilyen témájú kérést küldött.
- Modellarchitektúra feltérképezése: A különböző figyelmi fejek (attention heads) vagy rétegek aktiválódása eltérő memóriahozzáférési mintázatokat generál. A támadás hosszabb távú megfigyeléssel segíthet feltérképezni a modell belső struktúráját, ami további támadások (pl. modell-lopás) előszobája lehet.
- Felhasználói viselkedés elemzése: Egy szolgáltató, amely több ügyfélnek kínál finomhangolt modelleket ugyanazon a hardveren, sebezhetővé válhat. Egyik ügyfél (támadó) elemezheti a másik (áldozat) modellhasználati szokásait, például megállapíthatja, hogy az áldozat modellje milyen típusú feladatokra van optimalizálva a memóriahozzáférési mintázatok alapján.
Védekezési stratégiák
A bérlők közötti szivárgás elleni védekezés komplex, mivel a teljesítmény és a biztonság közötti kompromisszumot igényli.
- Hardveres izoláció: A legbiztosabb, de legdrágább megoldás a fizikai erőforrások dedikálása. Ha minden bérlő saját, dedikált CPU-magot és GPU-t kap, a megosztott gyorsítótárakból eredő támadási felület megszűnik.
- Gyorsítótár-tisztítás (Cache Flushing): Minden kontextusváltáskor (amikor az egyik bérlő folyamata után egy másik következik) a rendszer kiürítheti a megosztott gyorsítótár releváns részeit. Ez hatékonyan megszünteti a támadási felületet, de jelentős teljesítménycsökkenéssel jár, mivel minden bérlőnek „hideg” gyorsítótárral kell indulnia.
- Zajosítás (Noisy Scheduling): Az ütemező szándékosan „zajt” vihet a rendszerbe azzal, hogy a bérlők folyamatait véletlenszerűen helyezi át a különböző CPU-magok között. Ez megnehezíti a támadó számára, hogy konzisztensen ugyanazon a hardveren fusson az áldozattal, így a mérései megbízhatatlanná válnak.
- Partitioning: A gyorsítótár felosztása a bérlők között (Cache Allocation Technology – CAT), ahol minden bérlő csak a számára kijelölt cache-részt használhatja. Ez csökkenti a támadási felületet, de bonyolultabb hardveres támogatást igényel.
Kulcsgondolat: A bérlők közötti szivárgás arra a kényelmetlen igazságra világít rá, hogy a szoftveres absztrakciók falai a fizikai hardver szintjén sokkal vékonyabbak, mint gondolnánk. Egy Red Teaming feladat során ezeknek a „réseknek” a felkutatása és kihasználása bizonyíthatja egy megosztott rendszer valós biztonsági kockázatait.