Bei der Entwicklung mit Django gibt es Situationen, in denen wir Daten über URL-Parameter, verborgene Felder in Formularen oder Cookies an den Client senden und sie später wieder empfangen müssen. Dabei kommt die Frage auf: "Könnte dieses Datenstück möglicherweise von einem Benutzer unterwegs verändert worden sein?" django.core.signing ist ein leistungsstarkes Werkzeug, das genau dieses Problem löst.

Dieses Modul bietet keine Verschlüsselung (Encryption), sondern kryptographische Signaturen (Cryptographic Signing).

  • Verschlüsselung (X): Versteckt den Inhalt der Daten.

  • Signatur (O): Der Inhalt der Daten ist möglicherweise sichtbar, garantiert jedoch, dass die Daten nicht verändert wurden.


1. Kernnutzung: dumps() und loads()



Der Kern des signing-Moduls sind dumps() und loads(). Sie wandeln Python-Objekte in signierte Strings um oder verifizieren und stellen signierte Strings wieder als Objekte her.

dumps(): Umwandlung eines Objekts in einen signierten String

dumps() akzeptiert ein serialisierbares Objekt wie Dictionaries oder Listen und gibt einen URL-sicheren signierten String zurück.

from django.core.signing import dumps

# Daten, die signiert werden sollen
user_data = {'user_id': 123, 'role': 'user'}

# Die Daten werden signiert und in einen String umgewandelt.
signed_data = dumps(user_data)

print(signed_data)
# Beispielausgabe: eyJ1c2VyX2lkIjoxMjMsInJvbGUiOiJ1c2VyIn0:1q51oH:F... (Daten:Signatur)

Das Ergebnis hat die Form [kodierte Daten]:[Signatur]. Der vordere Teil ist die Base64-codierte Version der Originaldaten, sodass jeder sie decodieren und die Originaldaten einsehen kann. Der hintere Teil ist die Signatur (Hash-Wert), die mit dem SECRET_KEY von Django erstellt wurde.

loads(): Wiederherstellung eines signierten Strings zu einem Objekt (Verifizierung)

loads() nimmt einen mit dumps() erzeugten String entgegen und verifiziert die Signatur. Ist die Signatur gültig, wird das Originalobjekt zurückgegeben; ist sie ungültig, wird eine BadSignature-Ausnahme ausgelöst.

from django.core.signing import loads, BadSignature

try:
    # Wiederherstellung der signierten Daten als Originalobjekt (Verifizierung)
    unsigned_data = loads(signed_data)
    print(unsigned_data)
    # Ausgabe: {'user_id': 123, 'role': 'user'}

except BadSignature:
    print("Die Daten wurden manipuliert oder die Signatur ist ungültig.")

# Wenn auch nur ein Zeichen geändert wurde?
tampered_data = signed_data.replace('user', 'admin')

try:
    loads(tampered_data)
except BadSignature:
    print("Manipulierte Daten lösen eine BadSignature-Ausnahme aus.")

Verwenden Sie immer den try...except BadSignature-Block.


2. Kernmerkmale: Wo werden die Daten gespeichert?

Das größte Merkmal von django.core.signing ist, dass es "Stateless (zustandslos)" ist.

Ein mit dumps() erzeugter String wird weder in der Datenbank noch im Cache des Servers gespeichert. Alle Informationen (Daten und Signatur) sind im signierten String selbst enthalten.

Der Server überträgt diesen String an den Client (z. B. über URL, Cookies oder Formularfelder) und verifiziert später den übermittelten Wert mit dem SECRET_KEY in Echtzeit. Das spart Speicherplatz auf dem Server und macht es sehr leicht und effizient.


3. Setzen der Gültigkeitsdauer: Das Geheimnis von max_age



"Wenn die Daten nicht auf dem Server gespeichert sind, wie kann dann eine Gültigkeitsdauer festgelegt werden?"

Die max_age-Option umfasst bei dumps() einen Zeitstempel (Zeitinformationen) in die Signatur.

Wenn Sie loads() aufrufen, können Sie den Parameter max_age (in Sekunden) übergeben, der nach der Signaturprüfung den eingebetteten Zeitstempel überprüft.

  1. Berechnen Sie die Zeitdifferenz zwischen der aktuellen Zeit und dem Zeitstempel.

  2. Wenn diese Differenz max_age überschreitet, wird eine SignatureExpired-Ausnahme ausgelöst, auch wenn die Signatur nicht manipuliert wurde.

from django.core.signing import dumps, loads, SignatureExpired, BadSignature
import time

# 1. Signatur erstellen (die Zeit zu diesem Zeitpunkt wird aufgezeichnet)
signed_data = dumps({'user_id': 456})

# 2. Verifizierung mit 10 Sekunden Gültigkeit (sofort) -> erfolgreich
try:
    data = loads(signed_data, max_age=10)
    print(f"Verifizierung erfolgreich: {data}")
except SignatureExpired:
    print("Die Signatur ist abgelaufen.")
except BadSignature:
    print("Die Signatur ist ungültig.")


# 3. 5 Sekunden warten
time.sleep(5)

# 4. Verifizierung mit 3 Sekunden Gültigkeit (bereits 5 Sekunden vergangen) -> fehlgeschlagen
try:
    data = loads(signed_data, max_age=3)
    print(f"Verifizierung erfolgreich: {data}")
except SignatureExpired:
    print("Die Signatur ist abgelaufen. (max_age=3)")
except BadSignature:
    print("Die Signatur ist ungültig.")

Dies ist ebenfalls eine nicht serverseitige Methode zur Speicherung der Ablaufzeit, sondern verwendet den im Signatur enthaltenen Zeitstempel in einem stateless-Ansatz.


4. Wichtig! Vorsichtsmaßnahmen und Nutzungstipps

  1. Der SECRET_KEY ist lebenswichtig.

    Alle diese Signaturmechanismen basieren auf dem SECRET_KEY in settings.py. Wenn dieser Schlüssel geleakt wird, kann jeder einen gültigen Signature erzeugen, und es besteht Gefahr, dass er nicht öffentlich zugänglich gemacht wird. (Laden Sie ihn nicht auf Git hoch!)

  2. Denken Sie daran, dass es keine Verschlüsselung ist.

    Der vordere Teil der signierten Daten (Base64) kann von jedermann leicht decodiert werden, um die Originalinhalte einzusehen. Setzen Sie niemals vertrauliche Daten wie Passwörter oder persönliche Informationen direkt in dumps(). (Beispiel: user_id ist in Ordnung, aber user_password nicht.)

  3. Trennen Sie Signaturen mit Salt.

    Wenn Sie Signaturen für unterschiedliche Zwecke verwenden, nutzen Sie den Salt-Parameter. Wenn der Salt unterschiedlich ist, wird das Signaturergebnis selbst bei denselben Daten vollkommen unterschiedlich sein.

# Unterschiedliche Salze je nach Verwendungszweck verwenden
pw_reset_token = dumps(user.pk, salt='password-reset')
unsubscribe_link = dumps(user.pk, salt='unsubscribe')
Auf diese Weise können Angriffe wie die Wiederverwendung einer Signatur für 'Abmeldelinks' bei 'Passwortzurücksetzungen' verhindert werden.

5. Hauptanwendungsbeispiele

  • Passwortzurücksetzung URL: Signieren der Benutzer-ID und Zeitstempel und per E-Mail versenden (keine temporäre Token im DB erforderlich)

  • E-Mail-Bestätigungslink: E-Mail-Bestätigungslink nach neuer Registrierung

  • Sichere next URL: Verhindert, dass die URL ?next=/private/ nach dem Login auf eine böswillige Seite umgeleitet wird

  • Temporärer Download-Link: Erzeugung eines Dateizugriffs-URL, der nur für einen bestimmten Zeitraum (max_age) gültig ist

  • Mehrstufige Formulare (Form Wizard): Verhinderung von Datenmanipulation bei der Übergabe von Formulardaten an den nächsten Schritt