Röviden: A PHP unserialize biztonságánál egy egyszerű szabály érvényes: a kívülről érkező adatokat soha ne dolgozza fel az unserialize()-zal. Használjon helyette JSON-t. Ahol az unserialize() régi kódból megmaradni kényszerül, korlátozza az allowed_classes => false beállítással, és az adatok eredetiségét ellenőrizze aláírással.
Mi romolhat el az unserialize()-nál
A PHP tömböket és objektumokat a serialize()-zal szöveggé alakíthat, és az unserialize()-zal visszaállíthat. Ez kényelmes az adatok cookie-kban, gyorsítótárakban vagy adatbázismezőkben való tárolására. A probléma: az unserialize() közben tetszőleges osztályok objektumait is létrehozza, amelyek az alkalmazásban betölthetők.
Egy ilyen objektum létrehozásakor és legkésőbb megsemmisítésekor a PHP automatikusan meghív bizonyos metódusokat, például a __wakeup()-ot, az __unserialize()-t vagy a __destruct()-ot. Ha a projektben vagy a könyvtárakban lévő osztályok ilyen metódusokat tartalmaznak fájl-, adatbázis- vagy rendszerműveletekkel, a támadó manipulált adatokkal visszaélhet velük. A szakemberek PHP Object Injectionről beszélnek. A meglévő osztályoktól függően a hatás egyes fájlok törlésétől a kártékony kód futtatásáig terjed.
Nem műszaki embereknek összefoglalva: egyetlen meggondolatlan hívás a rossz helyen messzire engedheti a támadót az alkalmazásban. Ez a sebezhetőség ezért évek óta az OWASP Top 10 témái közé tartozik.
A biztonságos alternatíva: JSON az unserialize helyett
Szinte minden esetben, amikor az adatok kívülről érkeznek vagy elhagyják a szervert, a JSON a helyes választás. A json_decode() csak tömböket, számokat, karakterláncokat és logikai értékeket hoz létre, saját osztályok objektumait nem.
<?php
declare(strict_types=1);
function leseEinstellungen(string $eingabe): array
{
try {
$daten = json_decode($eingabe, true, 16, JSON_THROW_ON_ERROR);
} catch (JsonException $e) {
throw new InvalidArgumentException('Ungültige Daten.');
}
if (!is_array($daten)) {
throw new InvalidArgumentException('Erwartet wurde ein Objekt.');
}
// Csak az ismert mezőket vegye át, a várt típusokkal
return [
'sprache' => in_array($daten['sprache'] ?? '', ['de', 'en'], true) ? $daten['sprache'] : 'de',
'seiten' => max(1, min(100, (int) ($daten['seiten'] ?? 20))),
];
}
A második rész a fontos: a JSON-adatok is bemenetek. Csak a várt mezőket vegye át, és ellenőrizze a típusokat és az értéktartományokat, mielőtt dolgozna velük.
Ha az unserialize elkerülhetetlen: allowed_classes és aláírás
A régebbi alkalmazásokban a szerializált adatok gyakran olyan adatbázisokban vagy gyorsítótárakban vannak, amelyeket nem lehet azonnal átállítani. Ekkor két lépésben korlátozza a kockázatot. Először az allowed_classes-szal megakadályozza, hogy objektumok jöjjenek létre. Másodszor egy HMAC-aláírással biztosítja, hogy az adatok ténylegesen az Ön alkalmazásától származnak, és útközben nem módosították őket.
<?php
declare(strict_types=1);
const SCHLUESSEL = 'aus-sicherer-konfiguration-laden'; // soha ne a kódtárolóban
function verpacken(array $daten): string
{
$roh = serialize($daten);
return hash_hmac('sha256', $roh, SCHLUESSEL) . '.' . base64_encode($roh);
}
function auspacken(string $paket): array
{
[$signatur, $b64] = array_pad(explode('.', $paket, 2), 2, '');
$roh = base64_decode($b64, true);
if ($roh === false || !hash_equals(hash_hmac('sha256', $roh, SCHLUESSEL), $signatur)) {
throw new RuntimeException('Signatur ungültig.');
}
$daten = unserialize($roh, ['allowed_classes' => false, 'max_depth' => 16]);
if (!is_array($daten)) {
throw new RuntimeException('Unerwartetes Format.');
}
return $daten;
}
Ha mégis szükség van bizonyos osztályokra, nevezze meg őket egyenként az allowed_classes-ban, ahelyett hogy mindet engedélyezné. A kulcsnak a szerverkonfigurációba vagy egy secret-tárolóba kell tartoznia, és egy incidens után meg kell újítani.
A meglévő adatok fokozatos átállítása JSON-ra
A kialakult alkalmazásokban a szerializált értékek gyakran évekig az adatbázisban vannak. Az egy lépésben végzett átállítás ilyenkor kockázatos. Egy átmenet vált be: az alkalmazás mindkét formátumot olvassa, de már csak JSON-t ír. Egy régi érték olvasásakor egyszer kicsomagolja az allowed_classes => false beállítással, és azonnal JSON-ként visszaírja. Egy idő után a legtöbb bejegyzés át van állítva, a maradékot pedig egy egyszeri szkript elvégzi.
Az átmenet alatt naplózza, milyen gyakran olvassák még a régi formátumot, és azt is, ha az aláírások nem egyeznek. Az ilyen kudarcok manipulációs kísérletekre utalhatnak, és a felügyeletbe tartoznak. Csak ha régi formátum már nem fordul elő, távolítsa el az unserialize() ágat teljesen a kódból.
Rejtett deszerializáció: munkamenetek, gyorsítótárak és fájlútvonalak
Nem minden deszerializáció látszik a saját kódban. Ellenőrizze ezeket a helyeket is:
- Munkamenetek: A PHP a session-adatokat automatikusan szerializálja. Amíg a támadók nem tudják írni a session-fájlokat, ez nem kritikus. A weboldalankénti külön session-útvonalakat a Munkamenetek védelme PHP-ban útmutató írja le.
- Session-szerializáló: A CVE-2026-115560-ra vonatkozó közlemény egy szokatlan konfigurációt érint, amelyben a
serialize_handlerértékét futásidőbenini_set()-tel állítják át, és a session upload progress aktív. A php.net szerinti megoldás:session.upload_progress.enabled=0a php.ini-ben vagy az FPM-poolban, utána a workerek újraindítása. - Gyorsítótárak és várakozási sorok: A Redis, a Memcached vagy a szerializált értékeket tartalmazó adatbázistáblák csak annyira biztonságosak, mint a hozzájuk vezető hozzáférés. Nem szabad nyitva lenniük a hálózat felé.
- Fájlfüggvények bemenetekből származó útvonalakkal: A fájlfüggvényeknek soha ne adjon át a kérésből származó útvonalakat. Az olyan stream wrapperek, mint a
phar://, különben váratlan feldolgozást válthatnak ki. A fájlnevek engedélylistája megbízhatóbban véd bármilyen szűrésnél. - Függőségek: Az ismert sebezhetőségekkel rendelkező könyvtárak bővítik a támadó lehetőségeit. Tartsa őket naprakészen, ahogy a Composer-függőségek ellenőrzése résekre útmutató leírja.
Kockázatos helyek keresése a kódban
Egy első leltár egy egyszerű kereséssel sikerül. Nem változtat a rendszeren, és megmutatja az összes hívást a vendor könyvtáron kívül:
grep -rn --include='*.php' 'unserialize(' /var/www/projekt \
| grep -v '/vendor/'
Minden találatot három kérdéssel értékeljen: Honnan származnak az adatok? Megváltoztathatja őket egy felhasználó? Be van állítva az allowed_classes? A cookie-kból, a $_GET-ből, a $_POST-ból, a $_REQUEST-ből vagy külső interfészekről származó bemenetekkel rendelkező találatok elsőbbséget élveznek. Cserélje őket JSON-ra, és tesztelje az átállítást, mielőtt kigördíti.
Egy további támpont a böngészőben van. A szerializált PHP-adatoknak tipikus felépítésük van típusazonosítókkal és hosszadatokkal. Ha ilyen értékeket talál a saját weboldala cookie-iban, rejtett űrlapmezőiben vagy paramétereiben, az alkalmazás szerializált adatokat adott át a látogatónak, és valószínűleg visszavárja őket. Éppen ezeket a helyeket kell ellenőrizni.
A vezetőségnek elég egy egyszerű kérdés a fejlesztőknek vagy az ügynökségnek: „Dolgoz fel az alkalmazásunk valahol kívülről érkező szerializált adatokat, és ha igen, hogyan vannak védve?“ Egy világos válasz lelőhelyekkel jó jel. Egy kitérő válasz ok arra, hogy ellenőriztesse az alkalmazást.
Hogyan segít konkrétan a PHP SOS?
- Átvizsgáljuk a kódját idegen adatokkal végzett deszerializáció után, és minden leletet az elérhetőség és a hatás szerint értékelünk.
- Konkrét átállításokat javasolunk JSON-ra vagy védett hívásokra, és a fejlesztőit végigkísérjük a megvalósításban.
- Ellenőrizzük a függőségeket ismert sebezhetőségek szempontjából, és dokumentáljuk az állapotot.
- Kívánságra az alkalmazást egy biztonsági audit és penetrációs teszt keretében függetlenül ellenőriztetjük.
Gyakori kérdések
Az unserialize() a PHP-ban alapvetően nem biztonságos?
Nem olyan adatokra, amelyeket csak a saját alkalmazása hoz létre, és senki sem változtathat meg. Veszélyessé akkor válik, ha felhasználók vagy harmadik felek befolyásolhatják a bemenetet, például cookie-kon, űrlapmezőkön vagy külső interfészeken keresztül.
Elég védelemnek az allowed_classes => false?
Megakadályozza tetszőleges objektumok létrehozását, és ezzel lezárja a legfontosabb támadási utat. Kívülről érkező bemenetekhez ennek ellenére a JSON a jobb választás, mert az elemző egyszerűbb, és nem rekonstruál objektumokat.
Segít a könyvtárak frissítése a deszerializációs támadások ellen?
Részben. A támadások gyakran olyan függőségekből származó osztályokat használnak, amelyek egy objektum megsemmisítésekor vagy felébresztésekor műveleteket hajtanak végre. Az aktuális csomagok lezárják az ismert láncokat, a tényleges ok azonban az unserialize-hívás idegen adatokkal.
Van köze a PHP2Shellnek a deszerializációhoz?
A két fő rés a PHP kéréskezelésében van. A CVE-2026-115560-ra vonatkozó közlemény ezzel szemben a munkamenet-szerializáció szokatlan konfigurációját érinti. A megoldás ott a session.upload_progress.enabled=0.