CodeGym /Corsi /JAVA 25 SELF /Sicurezza della serializzazione: best practice

Sicurezza della serializzazione: best practice

JAVA 25 SELF
Livello 43, Lezione 4
Disponibile

1. Best practice fondamentali per una serializzazione sicura

La serializzazione è come preparare i bagagli in aeroporto: se non sai cosa c’è dentro e a chi affidi la valigia, potresti avere brutte sorprese ai controlli di sicurezza. In Java la serializzazione consente di salvare e ripristinare facilmente gli oggetti, ma apre anche la porta a un intero spettro di attacchi quando i dati provengono da fonti non affidabili.

Minaccia classica:

La serializzazione in Java può essere non sicura. Se un attaccante invia un flusso malevolo, durante la deserializzazione possono verificarsi conseguenze spiacevoli: dalla modifica dei campi all’esecuzione di codice indesiderato. Non è una storiella da manuale — nella storia di Java ci sono stati davvero casi in cui gli attacchi si basavano proprio su questo meccanismo.

Perché succede?

Il punto è che la deserializzazione non è solo il ripristino dei valori dei campi. Nel processo viene creato un oggetto completo: possono essere chiamati metodi speciali (ad esempio, readObject, readResolve) e talvolta — si attivano punti vulnerabili del codice tramite reflection. Sono particolarmente pericolose le classi di librerie di terze parti: alcune eseguono azioni già in fase di deserializzazione. Perciò non fidarti mai dei dati serializzati ricevuti dall’esterno.

Usa transient per i dati sensibili

Se nella tua classe ci sono campi che contengono password, token, chiavi private o altre informazioni sensibili, dichiarali come transient. Questi dati non finiranno nel flusso serializzato.

import java.io.Serializable;

public class User implements Serializable {
    private String username;
    private transient String password; // non viene serializzato

    // ...costruttori, getter, setter...
}

Cosa succede in deserializzazione? Il campo password avrà il valore predefinito (null per le stringhe). È un bene: le password non verranno archiviate in file o inviate sulla rete.

Definisci esplicitamente serialVersionUID

Specifica sempre esplicitamente serialVersionUID. Questo riduce la probabilità di errori di compatibilità e minimizza il rischio di sostituzione delle classi durante la deserializzazione.

private static final long serialVersionUID = 1L;

Perché è importante per la sicurezza? Se non specifichi serialVersionUID, il compilatore lo genererà automaticamente in base alla struttura della classe. Ciò può portare a incongruenze inaspettate e, in teoria, ad abusi con la sostituzione di classi aventi lo stesso nome ma struttura diversa.

Verifica i tipi degli oggetti durante la deserializzazione

Non fidarti di ciò che arriva dalla rete o da un file. Dopo la deserializzazione controlla sempre che l’oggetto ottenuto abbia il tipo atteso prima di lavorarci.

Object obj = objectInputStream.readObject();
if (obj instanceof User) {
    User user = (User) obj;
    // è sicuro lavorare con user
} else {
    // tipo imprevisto — lanciare un'eccezione o gestire l'errore
}

A cosa serve? Un flusso malevolo può contenere un oggetto di un’altra classe che implementa Serializable, ma non corrisponde alla tua logica di business.

Limita le classi che possono essere deserializzate (ObjectInputFilter)

A partire da Java 9, utilizza i filtri — ObjectInputFilter — per limitare l’insieme di classi consentite alla deserializzazione. È come un controllo all’ingresso.

Esempio: impostazione del filtro

import java.io.*;

ObjectInputFilter filter = ObjectInputFilter.Config.createFilter(
        "com.example.User;com.example.Address;!*"
);

ObjectInputStream in = new ObjectInputStream(inputStream);
in.setObjectInputFilter(filter);

Object obj = in.readObject(); // adesso vengono deserializzati solo User e Address

Questo filtro consente solo le classi User e Address della tua applicazione. Tutte le altre verranno bloccate — verrà lanciata un’eccezione. Questo riduce sensibilmente il rischio di far entrare un oggetto malevolo.

Non deserializzare dati provenienti da fonti non affidabili

Regola d’oro: se non sei sicuro della fonte dei dati — non deserializzare. Preferisci formati che non eseguono codice durante il parsing (ad esempio, JSON, XML con parser sicuri).

Esempio di cattiva pratica:

// Non fatelo mai con dati da Internet!
ObjectInputStream in = new ObjectInputStream(socket.getInputStream());
Object obj = in.readObject(); // pericoloso!

Cosa fare meglio?

  • Usare parser JSON (ad esempio Gson/Jackson) oppure parser XML con validazione.
  • Se la serializzazione binaria è necessaria — filtrate le classi tramite ObjectInputFilter e verificate i tipi (instanceof).

Usa formati alternativi per lo scambio con sistemi esterni

Per le integrazioni, utilizza formati che non eseguono codice in fase di parsing: JSON, XML, Protocol Buffers, ecc. Questo quasi elimina gli attacchi via deserializzazione.

// Invece di ObjectInputStream usate un parser JSON
User user = gson.fromJson(jsonString, User.class);

Non archiviare oggetti serializzati in luoghi accessibili a tutti

I file con oggetti serializzati possono contenere dati sensibili. Non conservarli in directory pubbliche e limita i permessi a livello di file system.

Non fare affidamento sulla serializzazione per il controllo dell'integrità

La serializzazione non garantisce integrità o autenticità dei dati. Usa firme digitali, checksum o cifratura quando le modifiche non sono ammissibili.

2. Pratica: esempio di ObjectInputFilter e dimostrazione di una vulnerabilità

Esempio di filtraggio delle classi

Supponiamo di avere la classe User:

import java.io.Serializable;

public class User implements Serializable {
    private static final long serialVersionUID = 1L;
    private String username;
    private transient String password;

    // ...costruttori, getter, setter...
}

Il filtro consente solo User:

ObjectInputFilter filter = ObjectInputFilter.Config.createFilter(
        "com.example.User;!*"
);
in.setObjectInputFilter(filter);

Ora, se qualcuno prova a inserire un oggetto di un’altra classe, la deserializzazione terminerà con un errore.

Dimostrazione di una potenziale vulnerabilità

Classe dannosa:

// Immaginiamo che qualcuno abbia infilato una classe del genere
public class Evil implements java.io.Serializable {
    static {
        System.out.println("Codice dannoso eseguito!");
        // qui potrebbe esserci di tutto...
    }
}

Se non filtri le classi, durante la deserializzazione potrebbe essere creato un oggetto Evil, e l’inizializzatore statico verrà eseguito al caricamento della classe — questa è già un’attacco reale.

4. Errori tipici nella messa in sicurezza della serializzazione

Errore n. 1: deserializzazione senza filtraggio e verifica del tipo. Spesso gli sviluppatori leggono un oggetto dal flusso e lo castano subito al tipo richiesto. Questo apre la porta agli attacchi. Usa ObjectInputFilter e verifica il tipo con instanceof.

Errore n. 2: memorizzare dati sensibili senza transient. Se dimentichi di dichiarare password/chiavi come transient, finiranno nel flusso e potrebbero fuoriuscire insieme al file.

Errore n. 3: assenza di serialVersionUID. Senza un serialVersionUID esplicito sono possibili errori di compatibilità inattesi e rischi legati alla sostituzione delle classi.

Errore n. 4: usare la serializzazione per lo scambio con sistemi esterni. La serializzazione binaria è comoda all’interno dell’applicazione (ad esempio, cache), ma è rischiosa per lo scambio esterno. Preferisci JSON/XML/Proto con parser sicuri.

Errore n. 5: ignorare l’integrità dei dati. La modifica dei byte in un file serializzato passerà inosservata. Applica firme digitali, checksum o cifratura.

1
Compito
JAVA 25 SELF, livello 43, lezione 4
Bloccato
Artefatti dell'Incantatore: Etichetta di Versione per i Tesori
Artefatti dell'Incantatore: Etichetta di Versione per i Tesori
1
Compito
JAVA 25 SELF, livello 43, lezione 4
Bloccato
Clienti CRM: Garanzia dell'integrità dei dati durante il caricamento
Clienti CRM: Garanzia dell'integrità dei dati durante il caricamento
1
Sondaggio/quiz
Configurazione della serializzazione, livello 43, lezione 4
Non disponibile
Configurazione della serializzazione
Configurazione della serializzazione
Commenti
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION