Unsichere Deserialisierung: Risiken und Schutz
Wie nicht vertrauenswürdige pickle-, Java- und PHP-unserialize-Daten gefährliches Verhalten auslösen können und wie man sie ersetzt oder begrenzt.
💡 Einfach erklärt (ELI5)
Serialisierung ist wie der Versand einer Bauanleitung für ein Modell. Die Deserialisierung folgt den Anweisungen und rekonstruiert die Daten. Manche Formate können dabei Klassen benennen oder Hooks aufrufen. Nicht vertrauenswürdige Anweisungen können so unbeabsichtigtes Verhalten auslösen. Die Auswirkung hängt von Format, Runtime, verfügbaren Klassen und Anwendungskontext ab.
Core Concepts & Key Terms
- Serialisierung / Deserialisierung
- Daten oder Objekte in eine gespeicherte oder übertragene Darstellung umwandeln und im Programm wieder parsen oder rekonstruieren.
- Deserialisierungshooks
- Callbacks wie Python __reduce__, Java readObject oder PHP __wakeup / __unserialize, die bei der Objektrekonstruktion laufen können.
- Gadget-Kette
- Abfolge bereits vorhandener Bibliotheksverhalten, die kombiniert einen unbeabsichtigten Effekt erzeugen kann. Verfügbarkeit hängt von Anwendung und Versionen ab.
- Deserialisierungsfilter
- Richtlinie, die Klassen oder Graphmetriken beim Lesen eines Objektstroms begrenzt. Sie mindert Risiko, macht native Objektströme aber nicht zur bevorzugten Eingabe.
How Unsafe Deserialization Can Become an Exploit Chain
Nicht vertrauenswürdige Bytes erreichen den Decoder
Cookie, API-Body, Queue-Nachricht oder Datensatz enthält vom Angreifer beeinflussbare Daten.
Die Anwendung ruft ein natives Objektformat auf
Der Code übergibt die Bytes an einen Decoder wie Python pickle, Java ObjectInputStream oder PHP unserialize().
Die Rekonstruktion löst formatspezifisches Verhalten aus
Je nach Format und erreichbaren Klassen können Konstruktoren, Hooks, Autoloading oder Bibliotheksmethoden vor der Anwendungsvalidierung laufen.
Die Auswirkung hängt vom erreichbaren Verhalten ab
Ein nutzbares Gadget oder Hook kann unbefugte Effekte erzeugen; andere Eingaben schlagen nur fehl oder verbrauchen Ressourcen. Codeausführung ist nicht automatisch.
Python / Flask: unsicheres Objektladen und primitive JSON-Validierung
# VULNERABLE: pickle can execute behavior during unpickling.
from flask import Flask, request
import pickle
app = Flask(__name__)
@app.post("/api/session")
def load_session():
# Never deserialize attacker-controlled bytes with pickle.
session = pickle.loads(request.get_data())
return {"user_id": session.user_id}
# SAFER: parse JSON primitives, then validate expected shape.
from flask import Flask, abort, request
app = Flask(__name__)
app.config["MAX_CONTENT_LENGTH"] = 16 * 1024
@app.post("/api/session")
def load_session():
payload = request.get_json()
if not isinstance(payload, dict):
abort(400, "Expected a JSON object")
user_id = payload.get("user_id")
if type(user_id) is not int or user_id <= 0:
abort(400, "Invalid user_id")
# Resolve authorization and session state server-side.
return {"user_id": user_id}
Checkliste zur Härtung
Auch JSON braucht Größen- und Strukturprüfungen. Bei alten Objektformaten reicht eine Klassenliste nicht als alleinige Grenze: Authentifizieren Sie die exakten Bytes vor der Deserialisierung und schützen Sie Schlüssel. Ein MAC verschlüsselt nicht und verhindert kein Replay.
- Deserialisieren Sie keine nicht vertrauenswürdigen nativen Objektströme mit Python pickle oder PHP unserialize; bevorzugen Sie primitive JSON-Daten mit Schema- und Größenprüfung.
- Wenn Java-Serialisierung noch nicht entfallen kann, konfigurieren Sie eine ObjectInputFilter-Allowlist und Graphlimits; Filter sind nicht überall automatisch aktiv.
- Authentifizieren Sie bei Altformaten die exakten Bytes vor der Deserialisierung mit einem geschützten MAC-Schlüssel; ein MAC ist keine Verschlüsselung und verhindert kein Replay.
- Halten Sie Sitzungsautorität serverseitig oder nutzen Sie ein gepflegtes frameworkgestütztes Signaturverfahren mit Ablaufzeit, Audience und Schlüsselrotation.