Der ultimative Leitfaden zu Python Pickle
Thomas Daniels
20. April 2022
0 Min. LesezeitBei der Anwendungsentwicklung müssen wir komplexe Daten (z. B. Objekte) häufig persistent speichern, damit sie in verschiedenen Laufzeitumgebungen verfügbar sind. Komplexe Datenstrukturen und Objekte persistent zu speichern, ist jedoch alles andere als einfach. In Python können Sie dafür die integrierte Pickle-Bibliothek verwenden. Pickle kann ein Python-Objekt in einen flachen Bytestrom serialisieren (Pickling) und einen Bytestrom wieder in ein Python-Objekt umwandeln (Unpickling).
Um komplexe Daten persistent zu speichern, müssen Sie sie als flachen Bytestrom darstellen, damit Sie sie auf einem Datenträger speichern oder über ein Netzwerk senden können. Dieser Vorgang, bei dem eine Objektstruktur in einen Bytestrom umgewandelt wird, heißt Marshaling oder Serialisierung. Wenn unsere Anwendung den Bytestrom liest oder empfängt, führt sie den umgekehrten Vorgang aus und wandelt ihn wieder in unsere Objektstruktur um. Das nennt man Unmarshaling oder Deserialisierung.
Hinweis: Da Pickle spezifisch für Python ist, lässt es sich in jeder Python-Anwendung einfach verwenden. Das bedeutet allerdings auch, dass Pickle nicht zum Datenaustausch zwischen Anwendungen verwendet werden kann, die in unterschiedlichen Programmiersprachen geschrieben wurden.
In diesem Artikel erfahren Sie, wie Sie Pickle sicher in Ihren Anwendungen verwenden. Pickle ist in vielen Situationen äußerst praktisch. Beachten Sie jedoch, dass Sie Daten nur entpacken sollten, wenn Sie ihnen vertrauen! Das Unpickling nicht vertrauenswürdiger Daten kann zur Ausführung beliebigen Codes führen und ist eine häufige Ursache kritischer Sicherheitslücken.

So verwenden Sie Python Pickle
Pickle ist einfach zu verwenden – legen wir also direkt los. Alle relevanten Funktionen zum Pickling und Unpickling befinden sich im Modul pickle. Importieren wir zunächst dieses Modul.
Wenn Sie ein Python-Objekt pickeln, können Sie es entweder direkt in eine Datei oder in ein Bytes-Objekt schreiben, das Sie später in Ihrem Code verwenden. In beiden Fällen genügt ein einfacher Methodenaufruf.
Um ein Objekt in eine Datei zu pickeln, rufen Sie pickle.dump(object, file) auf. Um lediglich die gepickelten Bytes zu erhalten, rufen Sie pickle.dumps(object) auf.
Wie bereits erwähnt, lässt sich Pickle in Python einfach verwenden. Sie können viele Datentypen pickeln, zum Beispiel:
Python-Primitivtypen
Verschachtelte Sammlungsstrukturen wie Tupel, Listen, Mengen und Dictionaries – sofern die Dictionaries nur pickelbare Objekte enthalten
Die meisten Klasseninstanzen
Die Python-Dokumentation enthält eine vollständige Liste der Objekte, die sich mit pickle serialisieren lassen.
Wenn Sie versuchen, ein nicht pickelbares Objekt zu pickeln, löst Python einen PicklingError aus.
Python-Pickle-Beispiele
Üben wir das Pickling. Im Folgenden sehen Sie eine Datenstruktur, die den Status eines fiktiven Spiels darstellt. Darin befindet sich ein Spieler an einem bestimmten Ort in der Spielwelt, der durch ein Koordinatentupel angegeben wird. Außerdem gibt es Hindernisse an bestimmten Orten, die ebenfalls als Tupel dargestellt werden. Der Spieler besitzt Gegenstände mit einem Namen und einem Preis. Die Klassen sehen so aus:
Erstellen wir nun einen konkreten Spielstatus und pickeln ihn in eine Datei.
Pickle ist ein binäres Format, das für Menschen nicht lesbar ist. Wenn wir uns also den Inhalt der neu erstellten Datei state.bin ansehen, werden wir kaum etwas erkennen. Zu sehen sind lediglich einige Bezeichner, etwa GameState und obstacles.
Nachdem wir den Spielstatus in einer Datei gespeichert haben, laden wir ihn wieder daraus, um unserem Spiel eine Speicher- und Fortsetzungsfunktion hinzuzufügen. Zu pickle.dump und pickle.dumps gibt es entsprechende Funktionen zum Unpickling: pickle.load(_file_) lädt ein gepickeltes Python-Objekt aus einer Datei. pickle.loads(_bytes_) erledigt dasselbe mit den angegebenen Bytes.
Wie Sie sehen, sind die Grundlagen des Picklings sehr einfach. Ein Methodenaufruf genügt, um ein Python-Objekt zu speichern und wieder zu laden.
So lässt sich Python Pickle ausnutzen
Wie bereits in der Einleitung erwähnt, sollten Sie Daten nur entpacken, wenn Sie ihnen vertrauen, denn das Modul pickle ist nicht sicher. Wenn Sie den Bytestrom eines Angreifers entpacken, könnte beliebiger Code ausgeführt werden. Das ist eine Folge der mächtigen Funktionen des Pickle-Formats.
Ein Python-Objekt kann mithilfe der speziellen Methode _reduce_ festlegen, wie es gepickelt werden soll. Diese Methode muss entweder einen String oder ein Tupel zurückgeben. Ein String steht für den Namen einer globalen Variablen. Ein Tupel steht für aufrufbaren Code (etwa eine Funktion oder Klasse), die Argumente für diesen Code sowie einige optionale Informationen, die für dieses Beispiel nicht relevant sind. Beim Unpickling wird der angegebene Code mit seinen Argumenten aufgerufen.
Mit diesem Wissen können Sie ein gepickeltes Objekt erstellen, das beim Unpickling eine beliebige Funktion Ihrer Wahl aufruft. Sie könnten beispielsweise während des Unpicklings mit der Funktion os.system einen Systembefehl ausführen oder mit eval beliebigen Python-Code ausführen!
Hier ein (harmloses) Beispiel für diesen Angriff mit der Funktion eval (die beim Laden lediglich die Funktion print aufruft):
Wenn wir diesen Code ausführen, wird die Zahl 3 in der Konsole ausgegeben, denn beim Unpickling dieser Daten wird eval("print(1+2)") ausgeführt.
So verwenden Sie Python Pickle sicher
Wenn Sie Daten von einem nicht vertrauenswürdigen Client entgegennehmen müssen, können Sie Pickle aufgrund der oben genannten Risiken nicht verwenden. Nutzen Sie stattdessen ein anderes Datenserialisierungsformat wie JavaScript Object Notation (JSON).
Das ist mit Pythons json-Modul möglich. Der Nachteil: Das Modul json ist deutlich weniger leistungsfähig als Pickle, da es komplexe Datentypen wie benutzerdefinierte Objekte standardmäßig nicht unterstützt. Auch die nativen Datentypen von JSON sind begrenzt. Für eine Python-Menge gibt es beispielsweise kein entsprechendes JSON-Format. Daher müssten Sie einen benutzerdefinierten Encoder für Mengen verwenden. JSON ist jedoch ein sicheres Format für den Umgang mit nicht vertrauenswürdigen Daten.
Angenommen, eine vertrauenswürdige Anwendung erzeugt gepickelte Daten, aber Sie können deren Integrität zwischen dem Pickling und dem Unpickling nicht gewährleisten. Vielleicht sind die Daten nicht vertrauenswürdig, weil Sie sie über ein unsicheres Netzwerk senden oder in einem persistenten Speicher ablegen, auf den Angreifer möglicherweise zugreifen können.
In beiden Fällen besteht eine Lösung darin, mithilfe eines HMAC eine kryptografische Signatur für die gepickelten Daten zu erstellen. Anschließend werden die Daten zusammen mit der Signatur gesendet oder gespeichert. Vor dem Unpickling kann der Empfänger die Signatur überprüfen und so die Integrität der Daten sicherstellen.
So erstellen Sie eine Signatur (mit SHA256):
HINWEIS: In der Praxis würden Sie Ihren geheimen Schlüssel sicher in einer serverseitigen Anwendung speichern, die niemals einer nicht vertrauenswürdigen Umgebung ausgesetzt ist. Speichern Sie einen geheimen Schlüssel bitte nicht direkt in Ihrer Codebasis. =)
Der Empfänger kann den erwarteten Digest für die gepickelten Daten berechnen und prüfen, ob er mit dem übermittelten Digest übereinstimmt.
In diesem Szenario können Angreifer die gepickelten Daten zwar manipulieren, aber ohne den geheimen Schlüssel können sie weder die Signatur erfolgreich fälschen noch den Empfänger dazu bringen, nicht vertrauenswürdigen Code auszuführen.
Abschließende Gedanken
Sie wissen nun, wie Sie mit Pythons Pickle-Modul komplexe Python-Objekte sicher serialisieren und deserialisieren – und das ist wirklich eine ziemlich große Gurke.
Denken Sie daran: Pickle kann zu kritischen Sicherheitslücken im Code führen. Entpacken Sie deshalb niemals Daten, denen Sie nicht vertrauen. Wenn Sie Daten von einem nicht vertrauenswürdigen Client entgegennehmen müssen, sollten Sie das sicherere JSON-Format verwenden. Und wenn Sie gepickelte Daten zwischen vertrauenswürdigen Anwendungen übertragen, aber zusätzliche Maßnahmen gegen Manipulation benötigen, erstellen Sie eine HMAC-Signatur, die Sie vor dem Unpickling überprüfen können.
Starten Sie mit Capture the Flag
Erfahren Sie in unserem virtuellen On-Demand-Workshop für Einsteiger, wie Sie Capture-the-Flag-Herausforderungen lösen.
