# Nebezpečná deserializácia v PHP: unserialize a bezpečnosť

> Bezpečne používať PHP unserialize: prečo sú objekty zo vstupov používateľov nebezpečné, ako chránia JSON, allowed_classes a podpisy a kde hľadať.

Zdroj: https://phpsos.de/sk/ratgeber/deserialisierung-php/  
Stav: 25.10.2026  
Poskytovateľ: PHP SOS, Núdzová pomoc pri PHP a bezpečnosť serverov. Okamžitá pomoc cez WhatsApp: https://wa.me/491736494914?text=Dobr%C3%BD%20de%C5%88%2C%20PHP%20SOS%2C%20potrebujeme%20pomoc%20s%20na%C5%A1%C3%ADm%20serverom%20s%20PHP.%20Spolo%C4%8Dnos%C5%A5%3A%20%E2%80%A6%20Web%3A%20%E2%80%A6

**Stručne:** Pri bezpečnosti unserialize v PHP platí jednoduché pravidlo: údaje, ktoré prichádzajú zvonku, nikdy nespracúvajte pomocou `unserialize()`. Použite namiesto toho JSON. Kde musí `unserialize()` zo starého kódu zostať, obmedzte ho pomocou `allowed_classes => false` a pravosť údajov overte podpisom.

## Čo sa môže pri unserialize() pokaziť

PHP dokáže polia a objekty pomocou `serialize()` premeniť na reťazec znakov a pomocou `unserialize()` ich obnoviť. Je to pohodlné na ukladanie údajov do cookies, cache alebo polí databázy. Problém: `unserialize()` pritom vytvára aj objekty ľubovoľných tried, ktoré sa dajú v aplikácii načítať.

Pri vytváraní a najneskôr pri zničení takéhoto objektu PHP automaticky volá určité metódy, napríklad `__wakeup()`, `__unserialize()` alebo `__destruct()`. Ak triedy v projekte alebo v knižniciach obsahujú takéto metódy so súborovými, databázovými alebo systémovými akciami, útočník ich môže cez zmanipulované údaje zneužiť. Odborníci hovoria o PHP Object Injection. Podľa existujúcich tried siaha účinok od zmazania jednotlivých súborov až po vykonanie škodlivého kódu.

Pre netechnikov zhrnuté: jediné neuvážené volanie na nesprávnom mieste môže pustiť útočníka hlboko do aplikácie. Táto slabina preto už roky patrí k témam OWASP Top 10.

## Bezpečná alternatíva: JSON namiesto unserialize

Takmer vo všetkých prípadoch, keď údaje prichádzajú zvonku alebo opúšťajú server, je správnou voľbou JSON. `json_decode()` vytvára iba polia, čísla, reťazce a logické hodnoty, nie objekty vlastných tried.

```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.');
    }

    // Prevziať iba známe polia s očakávanými typmi
    return [
        'sprache' => in_array($daten['sprache'] ?? '', ['de', 'en'], true) ? $daten['sprache'] : 'de',
        'seiten'  => max(1, min(100, (int) ($daten['seiten'] ?? 20))),
    ];
}
```

Dôležitá je druhá časť: aj údaje JSON sú vstupy. Prevezmite iba očakávané polia a skontrolujte typy a rozsahy hodnôt, skôr než s nimi budete pracovať.

## Keď je unserialize nevyhnutné: allowed_classes a podpis

V starších aplikáciách sú serializované údaje často v databázach alebo cache, ktoré sa nedajú hneď prestaviť. Riziko potom obmedzíte v dvoch krokoch. Po prvé pomocou `allowed_classes` zabránite vytváraniu objektov. Po druhé podpisom HMAC zaistíte, že údaje skutočne pochádzajú z vašej aplikácie a cestou sa nezmenili.

```php
<?php
declare(strict_types=1);

const SCHLUESSEL = 'aus-sicherer-konfiguration-laden'; // nikdy v repozitári kódu

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;
}
```

Ak sa určité triedy predsa potrebujú, uveďte ich jednotlivo v `allowed_classes`, namiesto povolenia všetkých. Kľúč patrí do konfigurácie servera alebo do úložiska tajomstiev a po incidente sa musí obnoviť.

### Existujúce údaje postupne prestaviť na JSON

V rozrastených aplikáciách ležia serializované hodnoty v databáze často roky. Prestavba na jeden krok je potom riskantná. Osvedčil sa prechod: aplikácia číta oba formáty, ale zapisuje už len JSON. Pri čítaní starej hodnoty sa jednorazovo rozbalí s `allowed_classes => false` a okamžite sa zapíše späť ako JSON. Po určitom čase je väčšina záznamov prestavená a zvyšok vybaví jednorazový skript.

Počas prechodu zaznamenávajte, ako často sa ešte číta starý formát, a aj to, keď podpisy nesedia. Takéto zlyhania môžu naznačovať pokusy o manipuláciu a patria do monitoringu. Až keď sa starý formát už neobjavuje, odstráňte vetvu `unserialize()` z kódu úplne.

## Skrytá deserializácia: session, cache a cesty k súborom

Nie každá deserializácia je viditeľná vo vlastnom kóde. Skontrolujte aj tieto miesta:

- **Session:** PHP serializuje údaje session automaticky. Pokiaľ útočníci nemôžu zapisovať súbory session, je to nekritické. Oddelené cesty pre session pre každý web popisuje príručka [Zabezpečiť session v PHP](https://phpsos.de/sk/ratgeber/session-sicherheit-php/).
- **Serializér session:** Upozornenie k [CVE-2026-115560](https://phpsos.de/sk/php2shell/cve-2026-115560/) sa týka nezvyčajnej konfigurácie, pri ktorej sa `serialize_handler` za behu prestaví cez `ini_set()` a je aktívny session upload progress. Náprava podľa php.net: `session.upload_progress.enabled=0` v php.ini alebo poole FPM, potom reštart workerov.
- **Cache a fronty:** Redis, Memcached alebo tabuľky databázy so serializovanými hodnotami sú také bezpečné ako prístup k nim. Nepatria otvorene do siete.
- **Súborové funkcie s cestami zo vstupov:** Súborovým funkciám nikdy nepredávajte cesty z požiadavky. Obaly streamov ako `phar://` môžu inak spustiť neočakávané spracovanie. Zoznam povolených názvov súborov tu chráni spoľahlivejšie než akékoľvek filtrovanie.
- **Závislosti:** Knižnice so známymi zraniteľnosťami rozširujú možnosti útočníka. Udržiavajte ich aktuálne, ako je opísané v príručke [Kontrola závislostí Composeru na zraniteľnosti](https://phpsos.de/sk/ratgeber/composer-abhaengigkeiten-pruefen/).

## Nájsť rizikové miesta v kóde

Prvú inventúru zvládnete jednoduchým hľadaním. Nič nemení na systéme a ukáže všetky volania mimo adresára `vendor`:

```bash
grep -rn --include='*.php' 'unserialize(' /var/www/projekt \
  | grep -v '/vendor/'
```

Každý nález posúďte tromi otázkami: odkiaľ údaje pochádzajú? Môže ich používateľ zmeniť? Je nastavené `allowed_classes`? Prednosť majú nálezy so vstupmi z cookies, `$_GET`, `$_POST`, `$_REQUEST` alebo externých rozhraní. Nahraďte ich JSON a prestavbu otestujte, skôr než ju nasadíte.

Ďalší záchytný bod je v prehliadači. Serializované údaje PHP majú typickú stavbu s identifikátormi typov a údajmi o dĺžke. Ak nájdete také hodnoty v cookies, skrytých poliach formulárov alebo parametroch vlastného webu, aplikácia odovzdala serializované údaje návštevníkovi a pravdepodobne ich očakáva späť. Práve tieto miesta treba preveriť.

Pre vedenie stačí jednoduchá otázka vývojárom alebo agentúre: „Spracúva naša aplikácia niekde serializované údaje, ktoré prichádzajú zvonku, a ak áno, ako sú chránené?“ Jasná odpoveď s uvedením miest je dobré znamenie. Vyhýbavá odpoveď je dôvodom nechať aplikáciu skontrolovať.

## Ako konkrétne pomáha PHP SOS?

- Prehľadáme váš kód kvôli deserializácii s cudzími údajmi a každý nález posúdime podľa dosiahnuteľnosti a dosahu.
- Navrhujeme konkrétne prechody na JSON alebo zabezpečené volania a sprevádzame vašich vývojárov pri realizácii.
- Závislosti kontrolujeme na známe zraniteľnosti a stav zdokumentujeme.
- Na želanie necháme aplikáciu nezávisle preveriť v rámci [bezpečnostného auditu a penetračného testu](https://phpsos.de/sk/leistungen/sicherheitsaudit-penetrationstest/).


## Časté otázky

**Je unserialize() v PHP zásadne nebezpečné?**  
Nie pre údaje, ktoré vytvára iba vaša vlastná aplikácia a ktoré nikto nemôže zmeniť. Nebezpečné to začne byť, len čo môžu vstup ovplyvniť používatelia alebo tretie strany, napríklad cez cookies, polia formulárov alebo externé rozhrania.

**Stačí allowed_classes => false ako ochrana?**  
Bráni vytváraniu ľubovoľných objektov, a tým uzatvára najdôležitejšiu cestu útoku. Pre vstupy zvonku je JSON napriek tomu lepšia voľba, pretože parser je jednoduchší a neobnovuje objekty.

**Pomáha aktualizácia knižníc proti útokom deserializáciou?**  
Čiastočne. Útoky často využívajú triedy zo závislostí, ktoré pri zničení alebo prebudení objektu vykonávajú akcie. Aktuálne balíky uzatvárajú známe reťazce, skutočnou príčinou je však volanie unserialize s cudzími údajmi.

**Súvisí PHP2Shell s deserializáciou?**  
Obe hlavné zraniteľnosti sú v spracovaní požiadaviek v PHP. Upozornenie k CVE-2026-115560 sa naopak týka nezvyčajnej konfigurácie serializácie session. Náprava je tam session.upload_progress.enabled=0.

## Zdroje

- [OWASP Cheat Sheet Series (Deserialization)](https://cheatsheetseries.owasp.org/)
- [OWASP Top 10](https://owasp.org/www-project-top-ten/)
- [php.net: bezpečnostné hlásenia](https://www.php.net/)
