CodeGym /Kurse /JAVA 25 SELF /Probleme der binären Serialisierung: Sicherheit, Kompatib...

Probleme der binären Serialisierung: Sicherheit, Kompatibilität

JAVA 25 SELF
Level 45 , Lektion 0
Verfügbar

1. Sicherheit der binären Serialisierung

Serialisierung in Java ist nicht einfach nur das Speichern von Objektfeldern. Sie ermöglicht, beliebige Objekte mit beliebigem Inhalt „wiederherzustellen“, sofern sie das Interface Serializable implementieren. Klingt praktisch! Doch wenn Ihre Anwendung Daten deserialisiert, die aus einer nicht vertrauenswürdigen Quelle stammen (etwa aus dem Netzwerk oder aus einer Datei, die von einem Angreifer manipuliert worden sein könnte), wird sie potenziell zum Angriffsziel.

Wie funktioniert das?

Während der Deserialisierung erzeugt Java Objekte auf Basis eines Bytestroms, ohne die Konstruktoren der Klassen aufzurufen. Wenn in der Klasse spezielle Methoden wie readObject implementiert sind, werden sie automatisch aufgerufen. Ein Angreifer kann den Bytestrom so „konstruieren“, dass beim Deserialisieren verwundbarer Code ausgeführt wird.

Beispiel: „Gadget-Chain“-Angriff

Stellen Sie sich eine Klasse vor, die bei der Deserialisierung einen externen Befehl startet (eine Datei liest oder eine Shell aufruft). Wenn ein Angreifer weiß, dass die Anwendung Objekte bestimmter Typen deserialisiert, kann er einen speziellen Bytestrom unterjubeln, der zur Ausführung schädlichen Codes führt. Solche Angriffe werden als „Gadget-Chain“ aufgebaut – eine Kette von Aufrufen, die in einer gefährlichen Operation endet.

Warum ist das so kritisch?

Deserialisierung ist ein Prozess, bei dem aus einem Datenstrom reale Objekte wiederhergestellt werden – und in diesem Moment kann beliebiger Code ausgeführt werden. Wenn die Daten aus einer nicht vertrauenswürdigen Quelle stammen, öffnet dies die Tür für Remote Code Execution (RCE). Große Unternehmen haben Empfehlungen zur Vermeidung unsicherer Deserialisierung veröffentlicht; in modernen Unternehmensprojekten ist binäre Serialisierung oft durch Sicherheitsrichtlinien untersagt.

Wie kann man sich schützen?

  • Deserialisieren Sie niemals Objekte aus nicht vertrauenswürdigen Quellen.
  • Verwenden Sie Whitelisting – eine explizite Positivliste zulässiger Typen für die Deserialisierung.
  • Bevorzugen Sie für externe Integrationen Textformate: JSON, XML.
  • Wenn Serialisierung unvermeidbar ist, verwenden Sie Bibliotheken mit Optionen für sicheres Deserialisieren (z. B. Jackson mit Typbeschränkungen).
  • Beschränken Sie die Nutzung benutzerdefinierter Serialisierungsmethoden (readObject, readResolve usw.), sofern Sie deren Sicherheit nicht sicherstellen können.

2. Kompatibilität von Klassenversionen

Binäre Serialisierung in Java ist eng an die Klassenstruktur gebunden. Sie serialisieren ein Objekt in Version 1.0, aktualisieren dann die Klasse (Feld hinzugefügt/entfernt) – der Versuch, das „alte“ Objekt in die neue Version zu deserialisieren, kann zu einem Fehler oder zu Datenverlust führen.

Wie bestimmt Java die Kompatibilität?

Dafür wird ein spezielles Feld verwendet – serialVersionUID. Das ist die Versionskennung der Klasse. Wenn das serialisierte Objekt eine andere serialVersionUID hat als die aktuelle Klasse, wird eine InvalidClassException ausgelöst und die Deserialisierung abgebrochen.

import java.io.Serializable;

public class User implements Serializable {
    private static final long serialVersionUID = 1L; // Version explizit angegeben

    private String name;
    private int age;
}

Wenn Sie die Struktur der Klasse ändern (zum Beispiel das Feld email hinzufügen) und die serialVersionUID nicht ändern, betrachtet Java die Klasse als kompatibel und versucht, das alte Objekt zu deserialisieren. Wenn Sie die serialVersionUID nicht explizit angeben, generiert die JVM sie automatisch auf Basis der Struktur – und jede Änderung führt zu Inkompatibilität.

Was passiert bei Nichtübereinstimmung/Übereinstimmung der Versionen?

Wenn die Kennungen nicht übereinstimmen, findet keine Deserialisierung statt: InvalidClassException. Wenn sie übereinstimmen, werden Felder nach Name und Typ zugeordnet: neue Felder erhalten Standardwerte (null, 0), entfernte werden ignoriert. Bei Änderungen von Typ oder Name eines Felds sind Fehler und eine fehlerhafte Interpretation der Daten möglich.

Praxis-Tipp. Legen Sie in serialisierbaren Klassen stets eine explizite serialVersionUID fest. Ändern Sie sie nur bei inkompatiblen Änderungen (Entfernen/Typänderung eines wichtigen Felds). Beim Hinzufügen neuer Felder kann die Kennung unverändert bleiben – die JVM verarbeitet alte Objekte korrekt.

Tabelle: Was passiert bei Klassenänderungen

Änderung in der Klasse Was passiert beim Deserialisieren?
Neues Feld hinzugefügt Erhält den Standardwert (0, null)
Feld entfernt Beim Lesen alter Daten ignoriert
Feldtyp geändert Exception oder inkorrekte Daten
Feldname geändert Altes Feld wird ignoriert, neues – Standardwert
serialVersionUID geändert Exception InvalidClassException

3. Einschränkungen der Standardserialisierung

Nicht alle Objekte lassen sich serialisieren

Felder mit den Modifizierern transient und static werden nicht serialisiert. static – weil es der Klasse und nicht dem Objekt gehört; transient – weil Sie die Serialisierung dieses Felds explizit untersagt haben.

Einige Objekte sind per Definition nicht serialisierbar: Thread, DB-Verbindungen, Sockets, Scanner usw. Wenn Ihre Klasse ein Feld dieses Typs hat und es nicht transient ist, erhalten Sie eine NotSerializableException.

import java.io.Serializable;
import java.util.Scanner;

public class Session implements Serializable {
    private transient Scanner scanner; // wird nicht serialisiert!
    private String login;
}

Probleme mit Performance und Erweiterbarkeit

Die Serialisierung großer Objektgraphen kann langsam sein und viel Speicher benötigen.

Das binäre Format eignet sich schlecht für Integrationen mit anderen Plattformen und Sprachen – es wird im Wesentlichen nur von Java „verstanden“.

Es ist schwer zu kontrollieren, was genau serialisiert wird, insbesondere bei tiefen Hierarchien und zyklischen Referenzen.

Probleme bei der Unterstützung alter Daten

Die langfristige Aufbewahrung binärer Abzüge ist riskant. Nach ein bis zwei Jahren ändert sich die Klassenstruktur, und alte Dateien lassen sich nicht mehr laden.

Wahre Begebenheit: „Wir haben vor drei Jahren einen serialisierten Benutzer-Cache gespeichert, die Anwendung aktualisiert – und jetzt können wir ihn nicht mehr laden. Hallo, verlorene Daten!“

4. Best Practices: So vermeiden Sie Fallstricke

  • Verwenden Sie binäre Serialisierung nur für interne Zwecke, bei denen Sie beide Seiten des Prozesses kontrollieren.
  • Nutzen Sie binäre Serialisierung nicht für externe Integrationen und die langfristige Speicherung wichtiger Daten.
  • Geben Sie in serialisierbaren Klassen stets eine explizite serialVersionUID an.
  • Kennzeichnen Sie Felder, die nicht serialisiert werden sollen, mit dem Modifizierer transient.
  • Für den Austausch mit externen Systemen – Textformate und moderne Bibliotheken: JSON, XML, Jackson, Gson, JAXB.
  • Für die Kompatibilität nutzen Sie Versionierung: Speichern Sie die Objektversion in der Klasse selbst und passen Sie die Verarbeitung beim Deserialisieren an.
  • Wenn Serialisierung nur für einen Cache benötigt wird, erzwingen Sie nicht um jeden Preis die Kompatibilität: Einen Cache neu zu berechnen ist meist einfacher.
  • Speichern Sie in serialisierbaren Objekten keine sensiblen Daten (Passwörter, Schlüssel) – Serialisierung verschlüsselt keine Daten.

5. Typische Fehler beim Umgang mit binärer Serialisierung

Fehler Nr. 1: Deserialisierung von Daten aus nicht vertrauenswürdigen Quellen. Der gefährlichste Fehler ist es, Objekte „von draußen“ (aus dem Netz, vom Benutzer, aus einer manipulierten Datei) anzunehmen und zu deserialisieren. Das führt direkt zu Schwachstellen bis hin zu RCE.

Fehler Nr. 2: Implizite Änderung der Klassenstruktur ohne Aktualisierung der serialVersionUID. Wenn die Kennung nicht explizit angegeben wird, generiert die JVM sie automatisch. Jede Strukturänderung (sogar die Reihenfolge der Felder) führt zu Inkompatibilität und dazu, dass alte Objekte nicht mehr geladen werden können.

Fehler Nr. 3: Versuch, Objekte mit nicht serialisierbaren Feldern zu serialisieren. Wenn eine Klasse ein Feld hat, das Serializable nicht implementiert und nicht transient ist, endet die Serialisierung mit einer Exception.

Fehler Nr. 4: Ablage temporärer oder sensibler Daten in serialisierbaren Objekten. Tokens, Passwörter, temporäre Ressourcen-Handles – all das kann unbeabsichtigt in der Datei landen.

Fehler Nr. 5: Einsatz binärer Serialisierung für die Langzeitablage und den Austausch zwischen Versionen. Schon nach dem ersten Klassen-Update ist das Risiko beschädigter Daten und von Kompatibilitätsproblemen hoch.

Fehler Nr. 6: Die Erwartung, dass static- und transient-Felder nach der Deserialisierung „wiederhergestellt“ werden. Diese Felder werden nicht serialisiert; nach dem Laden haben sie ihre Standardwerte.

Kommentare
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION