# Deserializzazione non sicura in PHP: unserialize e sicurezza

> Usare unserialize in PHP in modo sicuro: perché gli oggetti da input degli utenti sono pericolosi, come proteggono JSON, allowed_classes e firme.

Fonte: https://phpsos.de/it/ratgeber/deserialisierung-php/  
Stato: 25/10/2026  
Fornitore: PHP SOS, Assistenza di emergenza PHP e sicurezza dei server. Assistenza immediata su WhatsApp: https://wa.me/491736494914?text=Buongiorno%20PHP%20SOS%2C%20abbiamo%20bisogno%20di%20aiuto%20con%20il%20nostro%20server%20PHP.%20Azienda%3A%20%E2%80%A6%20Sito%20web%3A%20%E2%80%A6

**In breve:** per la sicurezza di unserialize in PHP vale una regola semplice: non elaborare mai con `unserialize()` dati che provengono dall'esterno. Usi invece JSON. Dove `unserialize()` deve restare per via di codice legacy, lo limiti con `allowed_classes => false` e verifichi l'autenticità dei dati con una firma.

## Che cosa può andare storto con unserialize()

PHP può trasformare array e oggetti in una stringa con `serialize()` e ripristinarli con `unserialize()`. È comodo per salvare dati in cookie, cache o campi del database. Il problema: `unserialize()` crea così anche oggetti di classi arbitrarie che possono essere caricate nell'applicazione.

Alla creazione e al più tardi alla distruzione di un simile oggetto, PHP richiama automaticamente determinati metodi, ad esempio `__wakeup()`, `__unserialize()` o `__destruct()`. Se classi del progetto o di librerie contengono tali metodi con azioni su file, database o sistema, un aggressore può usarle in modo improprio tramite dati manipolati. Gli esperti parlano di PHP Object Injection. A seconda delle classi presenti, l'effetto va dalla cancellazione di singoli file all'esecuzione di codice dannoso.

In sintesi per i non tecnici: una sola chiamata sconsiderata nel punto sbagliato può lasciare entrare un aggressore in profondità nell'applicazione. Questa debolezza rientra perciò da anni tra i temi della OWASP Top 10.

## L'alternativa sicura: JSON invece di unserialize

Per quasi tutti i casi in cui i dati arrivano dall'esterno o lasciano il server, JSON è la scelta giusta. `json_decode()` crea solo array, numeri, stringhe e valori booleani, non oggetti di classi proprie.

```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.');
    }

    // Accettare solo i campi noti con i tipi attesi
    return [
        'sprache' => in_array($daten['sprache'] ?? '', ['de', 'en'], true) ? $daten['sprache'] : 'de',
        'seiten'  => max(1, min(100, (int) ($daten['seiten'] ?? 20))),
    ];
}
```

È importante la seconda parte: anche i dati JSON sono input. Accetti solo i campi attesi e verifichi tipi e intervalli di valori prima di lavorarci.

## Quando unserialize è inevitabile: allowed_classes e firma

Nelle applicazioni più vecchie i dati serializzati si trovano spesso in database o cache che non si possono convertire subito. Allora limiti il rischio in due passi. Primo, impedisca con `allowed_classes` la creazione di oggetti. Secondo, garantisca con una firma HMAC che i dati provengano davvero dalla Sua applicazione e non siano stati modificati lungo il percorso.

```php
<?php
declare(strict_types=1);

const SCHLUESSEL = 'aus-sicherer-konfiguration-laden'; // mai nel repository del codice

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;
}
```

Se servono comunque determinate classi, le indichi una per una in `allowed_classes`, invece di consentirle tutte. La chiave va nella configurazione del server o in un archivio di segreti e deve essere rinnovata dopo un incidente.

### Convertire gradualmente i dati esistenti a JSON

Nelle applicazioni cresciute negli anni i valori serializzati restano spesso per anni nel database. Una conversione in un solo passo è allora rischiosa. Si è dimostrato valido un passaggio graduale: l'applicazione legge entrambi i formati, ma scrive solo più JSON. Alla lettura di un vecchio valore, questo viene decompresso una sola volta con `allowed_classes => false` e subito riscritto come JSON. Dopo un po' la maggior parte delle voci è convertita e uno script una tantum si occupa del resto.

Durante il passaggio registri quanto spesso viene ancora letto il vecchio formato e anche quando le firme non corrispondono. Tali fallimenti possono indicare tentativi di manipolazione e rientrano nel monitoraggio. Solo quando non compare più alcun vecchio formato, rimuova del tutto dal codice il ramo con `unserialize()`.

## Deserializzazione nascosta: sessioni, cache e percorsi di file

Non ogni deserializzazione compare in modo visibile nel codice proprio. Controlli anche questi punti:

- **Sessioni:** PHP serializza automaticamente i dati di sessione. Finché gli aggressori non possono scrivere i file di sessione, non è critico. Percorsi di sessione separati per ogni sito sono descritti nella guida [Mettere in sicurezza le sessioni in PHP](https://phpsos.de/it/ratgeber/session-sicherheit-php/).
- **Serializzatore delle sessioni:** l'avviso su [CVE-2026-115560](https://phpsos.de/it/php2shell/cve-2026-115560/) riguarda una configurazione insolita in cui il `serialize_handler` viene cambiato durante l'esecuzione con `ini_set()` e l'upload progress delle sessioni è attivo. Misura secondo php.net: `session.upload_progress.enabled=0` nel php.ini o nel pool FPM, poi riavvio dei worker.
- **Cache e code:** Redis, Memcached o tabelle di database con valori serializzati sono sicuri quanto l'accesso a essi. Non vanno esposti apertamente in rete.
- **Funzioni sui file con percorsi da input:** non passi mai alle funzioni sui file percorsi presi dalla richiesta. Gli stream wrapper come `phar://` possono altrimenti innescare elaborazioni inattese. Un elenco di consentiti per i nomi dei file protegge qui in modo più affidabile di qualsiasi filtraggio.
- **Dipendenze:** le librerie con debolezze note ampliano le possibilità di un aggressore. Le tenga aggiornate, come descritto nella guida [Verificare le vulnerabilità delle dipendenze Composer](https://phpsos.de/it/ratgeber/composer-abhaengigkeiten-pruefen/).

## Trovare i punti a rischio nel codice

Un primo inventario riesce con una semplice ricerca. Non modifica nulla nel sistema e mostra tutte le chiamate al di fuori della directory `vendor`:

```bash
grep -rn --include='*.php' 'unserialize(' /var/www/projekt \
  | grep -v '/vendor/'
```

Valuti ogni riscontro con tre domande: da dove vengono i dati? Un utente può modificarli? `allowed_classes` è impostato? I riscontri con input da cookie, `$_GET`, `$_POST`, `$_REQUEST` o interfacce esterne hanno la precedenza. Li sostituisca con JSON e provi la conversione prima di rilasciarla.

Un ulteriore indizio si trova nel browser. I dati PHP serializzati hanno una struttura tipica con identificatori di tipo e indicazioni di lunghezza. Se trova tali valori in cookie, campi nascosti dei moduli o parametri del Suo sito, l'applicazione ha consegnato dati serializzati al visitatore e presumibilmente se li aspetta di ritorno. Proprio questi punti vanno controllati.

Per la direzione basta una semplice domanda a sviluppatori o agenzia: «La nostra applicazione elabora da qualche parte dati serializzati che provengono dall'esterno e, in caso affermativo, come sono protetti?» Una risposta chiara con i punti in cui si trovano è un buon segno. Una risposta evasiva è un motivo per far verificare l'applicazione.

## Come aiuta concretamente PHP SOS?

- Cerchiamo nel Suo codice la deserializzazione con dati estranei e valutiamo ogni ritrovamento per raggiungibilità e impatto.
- Proponiamo passaggi concreti a JSON o chiamate protette e affianchiamo i Suoi sviluppatori nell'attuazione.
- Verifichiamo le dipendenze alla ricerca di debolezze note e documentiamo lo stato.
- Su richiesta facciamo verificare l'applicazione in modo indipendente nell'ambito di un [audit di sicurezza e penetration test](https://phpsos.de/it/leistungen/sicherheitsaudit-penetrationstest/).


## Domande frequenti

**unserialize() in PHP è insicuro per principio?**  
Non per dati che genera solo la Sua applicazione e che nessuno può modificare. Diventa pericoloso non appena utenti o terzi possono influenzare l'input, ad esempio tramite cookie, campi di moduli o interfacce esterne.

**allowed_classes => false basta come protezione?**  
Impedisce la creazione di oggetti arbitrari e chiude così la via d'attacco più importante. Per input dall'esterno JSON resta comunque la scelta migliore, perché il parser è più semplice e non ricostruisce oggetti.

**Un aggiornamento delle librerie aiuta contro gli attacchi di deserializzazione?**  
In parte. Gli attacchi sfruttano spesso classi delle dipendenze che eseguono azioni quando un oggetto viene distrutto o risvegliato. I pacchetti aggiornati chiudono le catene note, ma la vera causa è la chiamata a unserialize con dati estranei.

**PHP2Shell ha qualcosa a che fare con la deserializzazione?**  
Le due vulnerabilità principali si trovano nell'elaborazione delle richieste di PHP. L'avviso su CVE-2026-115560 riguarda invece una configurazione insolita della serializzazione delle sessioni. La misura lì è session.upload_progress.enabled=0.

## Fonti

- [OWASP Cheat Sheet Series (Deserialization)](https://cheatsheetseries.owasp.org/)
- [OWASP Top 10](https://owasp.org/www-project-top-ten/)
- [php.net: bollettini di sicurezza](https://www.php.net/)
