# Nesigurna deserijalizacija u PHP-u: unserialize i sigurnost

> Sigurna uporaba PHP unserialize: zašto su objekti iz korisničkih unosa opasni, kako štite JSON, allowed_classes i potpisi i gdje tražiti u kodu.

Izvor: https://phpsos.de/hr/ratgeber/deserialisierung-php/  
Stanje: 25.10.2026  
Pružatelj: PHP SOS, PHP hitna pomoć i sigurnost poslužitelja. Hitna pomoć putem WhatsAppa: https://wa.me/491736494914?text=Pozdrav%20PHP%20SOS%2C%20trebamo%20pomo%C4%87%20s%20na%C5%A1im%20PHP%20poslu%C5%BEiteljem.%20Tvrtka%3A%20%E2%80%A6%20Web-stranica%3A%20%E2%80%A6

**Ukratko:** Kod sigurnosti PHP unserialize vrijedi jednostavno pravilo: podatke koji dolaze izvana nikad ne obrađivati s `unserialize()`. Umjesto toga koristite JSON. Gdje `unserialize()` iz starog koda mora ostati, ograničite ga s `allowed_classes => false` i autentičnost podataka provjeravajte potpisom.

## Što kod unserialize() može poći po zlu

PHP može nizove i objekte pretvoriti u znakovni niz s `serialize()` i vratiti s `unserialize()`. To je zgodno za pohranu podataka u kolačiće, cacheve ili polja baze podataka. Problem: `unserialize()` pritom stvara i objekte proizvoljnih klasa koje se u aplikaciji mogu učitati.

Pri stvaranju i najkasnije pri uništavanju takvog objekta PHP automatski poziva određene metode, primjerice `__wakeup()`, `__unserialize()` ili `__destruct()`. Ako klase u projektu ili u bibliotekama imaju takve metode s radnjama nad datotekama, bazom podataka ili sustavom, napadač ih može zloupotrijebiti putem manipuliranih podataka. Stručnjaci govore o PHP Object Injectionu. Ovisno o postojećim klasama, učinak seže od brisanja pojedinih datoteka do izvršavanja zlonamjernog koda.

Za netehničke osobe ukratko: jedan jedini nepromišljen poziv na pogrešnom mjestu može napadača pustiti duboko u aplikaciju. Ta slabost zato godinama spada među teme OWASP Top 10.

## Sigurna alternativa: JSON umjesto unserialize

Za gotovo sve slučajeve u kojima podaci dolaze izvana ili napuštaju poslužitelj, JSON je pravi izbor. `json_decode()` stvara samo nizove, brojeve, znakovne nizove i logičke vrijednosti, a ne objekte vlastitih klasa.

```php
<?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.');
    }

    // Preuzeti samo poznata polja s očekivanim tipovima
    return [
        'sprache' => in_array($daten['sprache'] ?? '', ['de', 'en'], true) ? $daten['sprache'] : 'de',
        'seiten'  => max(1, min(100, (int) ($daten['seiten'] ?? 20))),
    ];
}
```

Važan je drugi dio: i JSON podaci su unosi. Preuzmite samo očekivana polja te provjerite tipove i raspone vrijednosti prije nego što s njima radite.

## Kad je unserialize neizbježan: allowed_classes i potpis

U starijim aplikacijama serijalizirani podaci često su u bazama podataka ili cacheovima koji se ne mogu odmah prebaciti. Tada rizik ograničite u dva koraka. Prvo, s `allowed_classes` sprječavate stvaranje objekata. Drugo, HMAC potpisom osiguravate da podaci zaista potječu iz Vaše aplikacije i da nisu promijenjeni na putu.

```php
<?php
declare(strict_types=1);

const SCHLUESSEL = 'aus-sicherer-konfiguration-laden'; // nikad u repozitoriju koda

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;
}
```

Ako se određene klase ipak trebaju, navedite ih pojedinačno u `allowed_classes` umjesto da dopustite sve. Ključ pripada u konfiguraciju poslužitelja ili pohranu tajni te ga nakon incidenta treba obnoviti.

### Postupni prelazak postojećih podataka na JSON

Kod razvijenih aplikacija serijalizirane vrijednosti često godinama stoje u bazi podataka. Prelazak u jednom koraku tada je rizičan. Pokazao se dobrim postupni prijelaz: aplikacija čita oba formata, ali piše samo JSON. Pri čitanju stare vrijednosti jednokratno se raspakira s `allowed_classes => false` i odmah zapiše natrag kao JSON. Nakon nekog vremena većina je zapisa prebačena, a jednokratna skripta obavlja ostatak.

Tijekom prijelaza bilježite koliko se često još čita stari format, kao i kad potpisi ne odgovaraju. Takvi neuspjesi mogu upućivati na pokušaje manipulacije i pripadaju u nadzor. Tek kad se stari format više ne pojavljuje, granu `unserialize()` potpuno uklonite iz koda.

## Skrivena deserijalizacija: sesije, cacheovi i putanje datoteka

Nije svaka deserijalizacija vidljiva u vlastitom kodu. Provjerite i ova mjesta:

- **Sesije:** PHP automatski serijalizira podatke sesije. Sve dok napadači ne mogu pisati u datoteke sesija, to je nekritično. Odvojene putanje sesija po web-stranici opisuje vodič [Osiguravanje sesija u PHP-u](https://phpsos.de/hr/ratgeber/session-sicherheit-php/).
- **Serijalizatori sesija:** Obavijest o [CVE-2026-115560](https://phpsos.de/hr/php2shell/cve-2026-115560/) tiče se neuobičajene konfiguracije u kojoj se `serialize_handler` tijekom rada prebacuje putem `ini_set()` i Session Upload Progress je aktivan. Zaobilazno rješenje prema php.netu: `session.upload_progress.enabled=0` u php.ini ili FPM poolu, potom ponovno pokretanje workera.
- **Cacheovi i redovi čekanja:** Redis, Memcached ili tablice baze podataka sa serijaliziranim vrijednostima sigurni su samo koliko i pristup njima. Ne pripadaju otvoreno u mrežu.
- **Funkcije za datoteke s putanjama iz unosa:** Funkcijama za datoteke nikad ne predajte putanje iz zahtjeva. Stream wrapperi poput `phar://` inače mogu izazvati neočekivanu obradu. Popis dopuštenih naziva datoteka tu štiti pouzdanije od bilo kakvog filtriranja.
- **Ovisnosti:** Biblioteke s poznatim slabostima proširuju mogućnosti napadača. Držite ih ažurnima, kako opisuje vodič [Provjera Composer ovisnosti na ranjivosti](https://phpsos.de/hr/ratgeber/composer-abhaengigkeiten-pruefen/).

## Pronalaženje rizičnih mjesta u kodu

Prvi popis uspijeva jednostavnom pretragom. Ona ništa ne mijenja u sustavu i prikazuje sve pozive izvan direktorija `vendor`:

```bash
grep -rn --include='*.php' 'unserialize(' /var/www/projekt \
  | grep -v '/vendor/'
```

Svaki pogodak ocijenite s tri pitanja: Odakle podaci dolaze? Može li ih korisnik mijenjati? Je li `allowed_classes` postavljen? Pogoci s unosima iz kolačića, `$_GET`, `$_POST`, `$_REQUEST` ili vanjskih sučelja imaju prednost. Zamijenite ih JSON-om i testirajte prelazak prije puštanja u rad.

Dodatno uporište nalazi se u pregledniku. Serijalizirani PHP podaci imaju tipičnu građu s oznakama tipova i duljinama. Ako takve vrijednosti pronađete u kolačićima, skrivenim poljima obrazaca ili parametrima vlastite web-stranice, aplikacija je serijalizirane podatke predala posjetitelju i vjerojatno ih očekuje natrag. Upravo ta mjesta treba provjeriti.

Za upravu je dovoljno jednostavno pitanje programerima ili agenciji: „Obrađuje li naša aplikacija negdje serijalizirane podatke koji dolaze izvana i, ako da, kako su zaštićeni?“ Jasan odgovor s mjestima pronalaska dobar je znak. Izbjegavajući odgovor razlog je da se aplikacija dade provjeriti.

## Kako PHP SOS konkretno pomaže?

- Pretražujemo Vaš kod na deserijalizaciju s tuđim podacima i ocjenjujemo svaki nalaz prema dostupnosti i učinku.
- Predlažemo konkretne prelaske na JSON ili osigurane pozive i pratimo Vaše programere pri provedbi.
- Provjeravamo ovisnosti na poznate slabosti i dokumentiramo stanje.
- Na željeno aplikaciju neovisno provjeravamo u okviru [sigurnosnog audita i penetracijskog testa](https://phpsos.de/hr/leistungen/sicherheitsaudit-penetrationstest/).


## Česta pitanja

**Je li unserialize() u PHP-u u osnovi nesiguran?**  
Ne za podatke koje stvara samo Vaša vlastita aplikacija i koje nitko ne može mijenjati. Opasno postaje čim korisnici ili treće strane mogu utjecati na unos, primjerice putem kolačića, polja obrazaca ili vanjskih sučelja.

**Je li allowed_classes => false dovoljna zaštita?**  
Sprječava stvaranje proizvoljnih objekata i time zatvara najvažniji put napada. Za unose izvana JSON je ipak bolji izbor jer je parser jednostavniji i ne rekonstruira objekte.

**Pomaže li ažuriranje biblioteka protiv napada deserijalizacijom?**  
Djelomično. Napadi često koriste klase iz ovisnosti koje pri uništavanju ili buđenju objekta izvršavaju radnje. Aktualni paketi zatvaraju poznate lance, ali stvarni je uzrok poziv unserialize s tuđim podacima.

**Ima li PHP2Shell veze s deserijalizacijom?**  
Dvije glavne ranjivosti su u obradi zahtjeva u PHP-u. Obavijest o CVE-2026-115560 pak se tiče neuobičajene konfiguracije serijalizacije sesija. Zaobilazno rješenje tamo je session.upload_progress.enabled=0.

## Izvori

- [OWASP Cheat Sheet Series (Deserialization)](https://cheatsheetseries.owasp.org/)
- [OWASP Top 10](https://owasp.org/www-project-top-ten/)
- [php.net: sigurnosne objave](https://www.php.net/)
