EN

Serialisierung in Java: Beispiele und Prävention

In diesem Artikel:

Unter Serialisierung versteht man die Umwandlung von Objekten in Byte-Streams, die gespeichert, gemeinsam genutzt und von Java-Maschinen verwendet werden können, um die Objekte zu rekonstruieren. Serialisierung von Objekten ist für die Entwicklung von Java-Software unerlässlich, da sie den Datenaustausch zwischen Geräten ermöglicht, die auf verschiedenen Hosts laufen, und so eine Plattformunabhängigkeit erzeugen. 

Die Serialisierung bietet Hackern jedoch eine Angriffsfläche, da sie Objekte in Byte-Streams bearbeiten oder bösartige Skripte in der Laufzeitumgebung einsetzen können, um unerwartete Dinge zu erzeugen. Java-Serialisierungsschwachstellen werden häufig als Einstiegspunkt für Remote Code Execution und Angriffe zur Datenmanipulation ausgenutzt.

In diesem Artikel werden Serialisierungsschwachstellen in Java, Faktoren, die zu einer Anfälligkeit führen, und Strategien zu ihrer Vermeidung erörtert.

Was ist Serialisierung in Java?

Mit Hilfe der Java-Serialisierung konvertiert ein Benutzer den Zustand von Objekten in ein binäres Format zur Speicherung in Datenbanken und physischen Dateiformaten. 

Die Serialisierung hilft den integrierten Entwicklungsumgebungen (IDEs) von Java, ein Standardformat für die primitiven Datentypen der Objekte zu erstellen, das von anderen virtuellen Java-Maschinen gespeichert, übertragen oder rekonstruiert werden kann. Der Mechanismus ermöglicht auch die Unabhängigkeit von Infrastrukturen, da das standardisierte Format den Datenaustausch über Programmiersprachen und Architekturen hinweg ermöglicht.

Wie die Serialisierung in Java funktioniert

Java bietet einen automatischen Serialisierungsmechanismus, der die Schnittstelle java.io.Serializable für jedes als serialisierbar markierte Klassenobjekt implementiert. Die Markierung weist die JVM an, ein serialisierbares Objekt mit Hilfe der Methode ObjectOutputStream.writeObject(Object) in den gewünschten Ausgabe-Byte-Stream zu schreiben. Die serialisierbare Schnittstelle implementiert einen dreistufigen Prozess:

  • Schreiben der Metadateninformationen der Klasse des Objekts
  • Rekursives Schreiben der Beschreibung dieser Klassenstruktur, bis java.lang.object gefunden wird
  • Rekursives Ausschreiben der mit dem Objektstrom verbundenen Daten, von den höchsten serialisierbaren Feldern bis zu den niedrigsten

Beispiel für die Serialisierung

Schritt 1: Der Standard-Serialisierungsprozess in Java beginnt mit der Implementierung der Schnittstelle Serializable in der übergeordneten Klasse. Im Folgenden finden Sie eine Beispielklassendefinition:

import java.io.Serializable;

class darwin implements Serializable {

int darwinVersion = 10;

}

class contain implements Serializable{

int containVersion = 11;

}

Darwin ist nun eine serialisierbare Klasse.

Schritt 2: Rufen Sie anschließend die Methode writeObject() der Klasse java.io.ObjectOutputStream auf. Diese Methode serialisiert das Objekt und schreibt es in die angegebene Datei. Der Code würde ähnlich wie folgt aussehen:

public class DarwinTest extends darwin implements Serializable {
int version = 66;
contain con = new contain();

public int getVersion() {
return version;
}
public static void main(String args[]) throws IOException {
FileOutputStream fos = new FileOutputStream("darwin.out");
ObjectOutputStream oos = new ObjectOutputStream(fos);
DarwinTest st = new DarwinTest();
oos.writeObject(st);
oos.flush();
oos.close();
}
}

Dies gibt den serialisierten Strom von Bytes in die Datei darwin.out aus, deren Inhalt etwa so aussehen würde:

AC ED 00 05 73 72 00 0A 53 65 72 69 61 6C 54 65
73 74 05 52 81 5A AC 66 02 F6 02 00 02 49 00 07
76 65 72 73 69 6F 6E 4C 00 03 63 6F 6E 74 00 09
4C 63 6F 6E 74 61 69 6E 3B 78 72 00 06 70 61 72
65 6E 74 0E DB D2 BD 85 EE 63 7A 02 00 01 49 00
0D 70 61 72 65 6E 74 56 65 72 73 69 6F 6E 78 70
00 00 00 0A 00 00 00 42 73 72 00 07 63 6F 6E 74
61 69 6E FC BB E6 0E FB CB 60 C7 02 00 01 49 00
0E 63 6F 6E 74 61 69 6E 56 65 72 73 69 6F 6E 78
70 00 00 00 0B

Wozu brauchen wir Serialisierung in Java?

Einige Anwendungsfälle für die Serialisierung in Java sind:

Zwischenspeicherung

Die Serialisierung von Objekten speichert den Zustand eines Objekts, wobei die Deserialisierung nur wenig Zeit in Anspruch nimmt, um das ursprüngliche Objekt neu zu erstellen, verglichen mit der Erstellung aus einer Java-Klasse, was beim Zugriff auf Objekte aus dem Cache-Speicher Zeit spart.

Replikation

Byte-Streams können problemlos von mehreren virtuellen Java-Maschinen gemeinsam genutzt werden, die sie in kürzester Zeit serialisieren. Dies ermöglicht die Bereitstellung mehrerer Objektklone auf verschiedenen Maschinen/Umgebungen zur einfachen Replikation.

Mandantenübergreifende Kommunikation

Sobald das ursprüngliche Objekt serialisiert wurde, können Datenstrukturen effizient über ein Netzwerk von Maschinen übertragen werden, was den Austausch von Informationen zwischen verschiedenen Umgebungen ermöglicht.

Persistenz

Nach der Serialisierung kann Java den Zustand eines beliebigen Objekts in einer Datenbank oder einem Dateisystem speichern, das später wieder verwendet werden kann.

Maschinenübergreifende Synchronisierung

Das Serialisierungsprotokoll erzeugt Byteströme, die über verschiedene Architekturen und Betriebssysteme hinweg funktionieren und eine Synchronisierung zwischen verschiedenen JVMs ermöglichen.

Schwachstellen bei der Serialisierung

Der Serialisierungsprozess ist für Java-Anwendungen von entscheidender Bedeutung, da er eine Standarddatenstruktur bereitstellt und die Portabilität von Objekten ermöglicht. Trotz der Vorteile, die die Serialisierung in Java bietet, sind solche Anwendungen anfällig für Serialisierungsschwachstellen, bei denen Angreifer bösartige Objekte in die Laufzeitumgebung einfügen können, um Benutzer- oder Systemdaten zu gefährden. Diese Objekte enthalten in der Regel Nutzdaten und private Methoden, die ausführbaren Code auf den Ziel-JVMs ausführen. Sobald der Angreifer die Kontrolle über die serialisierten und deserialisierten Daten hat, kann er Programmobjekte und speicherinterne Variablen kontrollieren und den Codefluss innerhalb der Anwendung beeinflussen. Die Anwendung ist auch dann anfällig für Angriffe, wenn das Objekt Klassen enthält, deren Verhalten während des Deserialisierungsprozesses geändert werden kann. In solchen Fällen können Angreifer vorhandene Datenstrukturen ausnutzen, ihren Inhalt ändern, vorhandene Variablen überschreiben und andere Schwachstellen innerhalb des Serialisierungsmechanismus ausnutzen.

Unsichere Serialisierungsangriffe – Risikofaktoren

Serialisierungsangriffe werden seltener automatisch durchgeführt, da viel manuelle Arbeit erforderlich ist, um ein bösartiges Objekt zu erstellen, das der Serialisierungsmechanismus als gültig betrachtet. Sobald der Hacker den Exploit auf einer JVM erfolgreich kompiliert hat, kann er bösartigen Code aus der Ferne auf allen anderen Rechnern ausführen, auf denen dieselbe Anwendung läuft. Sobald die Angreifer die Kontrolle über den Quellcode erlangt haben, können sie die verwundbare Anwendung vollständig übernehmen. In solchen Fällen ist die Erkennung eines erfolgreichen Angriffs sehr komplex.

Mögliche Faktoren, die Serialisierungsangriffe in Java verursachen, sind unter anderem:

Deserialisierung von Objekten aus nicht vertrauenswürdigen Quellen

Die meisten Angriffe erfolgen, weil die Entwicklungs- und Betriebsteams es versäumen, die vom Benutzer bereitgestellten Daten vor der Deserialisierung eines Objekts zu validieren. Bei Anwendungen, die sich auf das binäre Serialisierungsformat verlassen, gehen die Entwickler davon aus, dass die Benutzer die Objektdaten nicht effektiv lesen und manipulieren können, so dass sie alle deserialisierten Objekte für vertrauenswürdig halten. Mit etwas Aufwand kann ein Angreifer Exploits entwickeln und bösartige Nutzdaten in binär serialisierte Objekte einschleusen, um die Anwendungslogik zu missbrauchen.

Eine große Anzahl von Abhängigkeiten

Typische Java-Anwendungen/Websites implementieren zahlreiche Bibliotheken, von denen jede ihre eigenen Abhängigkeiten hat. Daher umfasst eine Anwendung viele Methoden und Klassen, die sich nur schwer sicher verwalten lassen. Dies macht es schwierig, einen Serialisierungsangriff vorherzusehen und zu verhindern, da ein Hacker jede beliebige Klasse replizieren und eine Methode aufrufen kann, um seine bösartige Nutzlast auszuführen. Hacker können auch mehrere unerwartete Methodenaufrufe miteinander verbinden, so dass die endgültigen Daten, die in der Senke (Dateisystem, Datenbank usw.) geparst werden, sich völlig von der ursprünglichen Eingabe unterscheiden. Da die Entwickler den Datenfluss in der Anwendung nicht vorhersehen können, ist es fast unmöglich, alle Sicherheitslücken zu schließen, die durch die Serialisierungsschwachstelle entstehen.

Angriffsmethoden für die Java-Serialisierung

Es gibt mehrere Möglichkeiten, unsichere Serialisierungsschwachstellen in Java auszunutzen. Dazu gehören:

Ändern von Objektattributen

Wenn der Angreifer ein gültiges serialisiertes Objekt beibehält, während er seine statischen Felder manipuliert, erzeugt der Deserialisierungsprozess ein serverseitiges Objekt mit geänderten Attributwerten. Hacker verwenden diese Technik hauptsächlich, um Benutzerzugriffsberechtigungen in HTTP-Anfragen zu identifizieren und zu ändern, was eine unbefugte Ausweitung der Berechtigungen ermöglicht. 

Ändern von Datentypen

Angreifer nutzen Serialisierungsfehler aus, um unerwartete Datentypen zu liefern und die Java-Anwendungslogik zu missbrauchen. In einem solchen Fall erstellt der Hacker bösartigen Code, der lose Vergleiche ausnutzt, um den Rechner so zu manipulieren, dass er vom Benutzer gelieferte Daten aus einem deserialisierten Objekt akzeptiert. Der Hacker kann diese Daten dann verwenden, um Sicherheitskontrollen wie Autorisierungs- und Sitzungsauthentifizierungsmechanismen zu umgehen. 

Erweitern der Anwendungsfunktionalität

Bei diesem Ansatz wird die Funktionalität einer Java-Website/Anwendung genutzt, um bösartige Prozesse auf einem Datensatz in einem deserialisierten Objekt auszuführen. Diese Funktionalität ist vollständig für den Benutzer zugänglich, und der Hacker könnte manuell oder automatisch Daten in riskante Methoden einspeisen. 

Verhinderung von Serialisierungsangriffen in Java

Einige Best Practices zur Vermeidung von Serialisierungsschwachstellen sind:

Nicht-native Formate verwenden

Entwickler sollten Standarddatenformate wie XML und JSON verwenden, um die Möglichkeit auszuschließen, dass Angreifer eine benutzerdefinierte Deserialisierungslogik für die Remotecodeausführung verwenden. Diese nicht-nativen Formate implementieren ein sicheres Datenübertragungsmuster, das ein separates Serialisierungsprotokoll für Objekte und Zustandsübertragungen erstellt, wodurch der Serialisierungsalgorithmus für externe Entitäten nicht verfügbar ist.

Verwendung von RASP zur Verhinderung der Remotecode-Ausführung

Runtime Application Self-Protection (RASP)-Tools werden in Anwendungsservern eingesetzt, um die gesamte Kommunikation zwischen dem Host und den Client-Rechnern abzufangen und Angriffe in Echtzeit zu erkennen. Diese Tools überwachen kontinuierlich Datenflüsse, System- und Benutzerverhalten, um bösartige Aktivitäten zu erkennen. Diese Tools erkennen auch Muster in Datenstrukturen und serialisierbaren Objekten, um einen Kontext für reguläres Benutzer- und Anwendungsverhalten zu schaffen. Jede Dateneingabe außerhalb des Kontextes wird als potenzielle Bedrohung betrachtet. RASP-Tools können so konfiguriert werden, dass sie Teams im Diagnosemodus vor Angriffsversuchen warnen oder im Schutzmodus die Angriffe abwehren. 

Implementierung von Prüfungen auf Datenintegrität/Deserialisierung signierter Daten

Ergänzen Sie Blacklisting- und Pattern-Matching-Techniken durch signaturbasierte positive Validierungstechniken, um sicherzustellen, dass die vom Benutzer bereitgestellten Daten vertrauenswürdig sind. Implementieren Sie außerdem ausreichende Autorisierungs-/Authentifizierungsprüfungen, um die Quelle des Eingabe-Byte-Stroms vor der Deserialisierung zu verifizieren. 

Verwenden Sie eigene Deserialisierungsmethoden

OWASP bietet ein Spickzettel mit Richtlinien zur Erstellung von benutzerdefiniertem Serialisierungs- und Deserialisierungscode, um sichere Validierungsmethoden aufzuzählen. Diese Richtlinien können verwendet werden, um sicherzustellen, dass die Anwendung frei von nicht vertrauenswürdigen Daten ist. 

Einschränkung der zu serialisierenden Klassen durch Unterklassifizierung von java.io.ObjectInputStream

Die Klasse java.io.ObjectInputStream deserialisiert Objekte, deren Sicherheit durch Unterklassifizierung gehärtet werden kann. Dies wird erreicht, indem die Funktion ObjectInputStream.html#resolveClass() überschrieben wird, die die Klassen einschränkt, die deserialisiert werden können. Sicherheitsteams können auch einen Agenten einsetzen, der die gesamte Verwendung der Klasse java.io.ObjectInputStream absichert, um Anwendungen gegen unbekannte bösartige Datentypen zu schützen. 

Einsatz eines Tools zur Erkennung von Sicherheitslücken

Setzen Sie ein Sicherheitstest-Tool ein, das statische und dynamische Anwendungsschwachstellen durch kontinuierliches Scannen und Testen identifiziert. Die Crashtest Security Suite hilft beim Scannen von APIs und Webanwendungen, um Serialisierungsschwachstellen zu identifizieren, bevor Angreifer sie ausnutzen. Melden Sie sich für eine kostenlose Testversion an, um ein schnelles und effizientes Schwachstellenscanning zu starten. 

Serialisierung in Java – FAQ

Was sind die Vorteile der Serialisierung?

Die Serialisierung ist in Java integriert und erfordert keine Integrationen von Drittanbietern. Der Prozess ist einfach zu erlernen, zu implementieren und für bestimmte Anwendungsfälle anzupassen. Die Serialisierung ermöglicht auch die Komprimierung, Verschlüsselung, Authentifizierung und sichere Datenverarbeitung in Java.

Sind Schwachstellen in der Serialisierung nur für Java-Anwendungen relevant?

Serialisierung ist ein globales Konzept, und jede Sprache, die Serialisierung implementiert, ist anfällig für Angriffsvektoren. Schwachstellen in der Serialisierung betreffen u. a. auch Ruby-, PHP- und Microsoft .NET-Anwendungen.

Erhalten Sie jetzt kostenlos einen schnellen Sicherheitsbericht für Ihre Website

Wir analysieren derzeit http://example.com
Wir scannen derzeit http://example.com
Status des Scans: In Bearbeitung
Scan target: http://example.com/laskdlaksd/12lklkasldkasada.a
Datum: 27/05/2022
Crashtest Security Suite prüft auf:
Information disclosure Known vulnerabilities SSL misconfiguration Open ports
Scanauftrag ausfüllen
Bitte geben Sie Ihre Daten ein, um die schnelle Sicherheitsüberprüfung zu erhalten.
Ein Sicherheitsspezialist analysiert gerade Ihren Scan-Bericht.
Bitte geben Sie Ihre Telefon-/Handynummer an, damit wir Ihre Identität überprüfen können:
Ihren Bericht anfordern
Vielen Dank.
Wir haben Ihren Antrag erhalten.
Sobald Ihr Sicherheitsaudit fertig ist, werden wir Sie benachrichtigen.