Nicht spürbar. Die Entschlüsselung passiert im Speicher für die Dauer einer Antwort und kostet unter 15 Millisekunden pro Download, weniger, als bei einer Dateiübertragung jemand wahrnimmt. Nichts wird im Klartext gecacht, also gibt es keine warme Kopie, die geschützt werden müsste, und nichts, das bei einem Schlüsselwechsel invalidiert werden muss.
Leistungen
Backend-Entwicklung, Security Engineering
Branche
B2B-Marktplatz
Jahr
2024
Verschlüsselung im Ruhezustand, ohne einen einzigen Download zu verändern
Die Rechtsabteilung eines B2B-Marktplatzes stellte eine Frage: Wenn jemand die Festplatte liest, was bekommt er? Die ehrliche Antwort war: alles. Tausende NDAs, Jahresabschlüsse und Unternehmensbewertungen lagen als normale Dateien in Djangos MEDIA_ROOT, lesbar für jeden mit einem geleakten SSH-Key oder einer Kopie des Backups. Unter der DSGVO war diese Antwort ein Haftungsrisiko.
DIE HERAUSFORDERUNG
Klartext in MEDIA_ROOT
Uploads waren normale Dateien im Media-Verzeichnis, benannt nach dem, was sie waren. Ein Angreifer brauchte nur Lesezugriff auf das Dateisystem, und dafür gab es drei plausible Wege: eine kompromittierte Abhängigkeit, einen geleakten SSH-Key oder ein Backup, das irgendwo landete, wo es nicht hingehörte. Jeder davon legte die Dokumente aller Kunden auf einmal offen. Es gab kein Zugriffs-Log, ein Einbruch hätte sich im Nachhinein nicht einmal beziffern lassen, und für das regulatorische Audit gab es keinen Nachweis für den Schutz ruhender Daten. Die Rechtsabteilung stufte das als kritisches DSGVO-Risiko ein. Die Produktseite war genauso fest: Am Hochladen und Herunterladen eines Dokuments durfte sich nichts ändern.
Dasselbe Verzeichnis, vorher und nachher
Was jemand mit Lesezugriff sieht. Zieh den Teiler: links die Auflistung, wie sie war, rechts dieselben Dateien nach der Verschlüsselungsschicht, Namen inklusive.


DIE LÖSUNG
Fernet, entschlüsselt pro Anfrage
Die Bibliothek bot zwei Wege. Rohes AES-GCM ist schneller und authentifiziert in einem Primitiv, überlässt dir aber die Nonce, und eine einzige wiederverwendete Nonce unter demselben Schlüssel bricht das ganze Verfahren. Fernet ist das High-Level-Rezept derselben Bibliothek: AES-128-CBC plus HMAC-SHA256, ein zufälliger IV pro Token, ein Versionsbyte und ein Zeitstempel, in einer API, die sich schwer falsch benutzen lässt. Ich habe Fernet gewählt und dafür mit zwei Durchläufen über jede Datei und ohne Streaming bezahlt: Ein Dokument wird als Ganzes im Speicher entschlüsselt, für die Dauer einer HTTP-Antwort, und die Datei auf der Festplatte ist zu keinem Zeitpunkt etwas anderes als Chiffrat. Der Schlüssel wird aus einem Master-Secret mit PBKDF2-HMAC über 100.000 Iterationen abgeleitet und lebt nur in einer Umgebungsvariable, nie in der Datenbank, lässt sich also ohne Deployment rotieren.
Schlüsselableitung und Verschlüsselung. Das Master-Secret kommt aus der Umgebung; der abgeleitete Schlüssel ist der, den Fernet benutzt:
Python
import base64
from cryptography.fernet import Fernet
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
from cryptography.hazmat.primitives import hashes
def get_fernet_key(master_secret: str) -> bytes:
"""Derive a Fernet key from the master secret via PBKDF2."""
kdf = PBKDF2HMAC(
algorithm=hashes.SHA256(),
length=32,
salt=b"stable-salt-from-settings",
iterations=100_000,
)
return base64.urlsafe_b64encode(
kdf.derive(master_secret.encode())
)
def encrypt_file(file_bytes: bytes, key: bytes) -> bytes:
"""Encrypt raw file bytes with Fernet."""
return Fernet(key).encrypt(file_bytes)In einem Fernet-Token
Tipp einen kurzen Text ein und verschlüssle ihn. Der Token entsteht in deinem Browser mit WebCrypto, mit derselben Konstruktion wie auf dem Server: Version, Zeitstempel, IV, Chiffrat, HMAC. Dann entschlüssle und achte auf die Reihenfolge: Der HMAC wird zuerst geprüft, ein gekipptes Byte wird abgewiesen, bevor AES überhaupt läuft. Rotiere den Schlüssel, und der alte Token öffnet sich trotzdem, weil MultiFernet jeden Schlüssel probiert, den es hält.
Läuft in deinem Browser mit WebCrypto. Nichts verlässt diese Seite.
- Version 0x80
- 1 Bytes
- IV
- 16 Bytes
- Chiffrat
- 48 Bytes
- HMAC-SHA256
- 32 Bytes
Token-Bytes nach Segment
Token, wie er auf der Festplatte liegt (base64url)
Entschlüsselungspfad
- HMAC über Version, Zeitstempel, IV und Chiffrat prüfen · wartet
Schlüsselring (MultiFernet)
- Schlüssel #1
0f5e2476aktuell
Neue Tokens nutzen den ersten Schlüssel. Beim Entschlüsseln wird jeder Schlüssel der Reihe nach probiert, bis ein HMAC passt.
- Token-Größe
- 105 B
- Klartext
- 38 B
- PKCS7-Padding
- 10 B
- Overhead
- 67 B
- Verschlüsselt mit
- Schlüssel #1
- Ergebnis
- Bereit
Die ausliefernde View: Pfadprüfung, Schlüsselzugriff mit rollenspezifischen Fehlern, Entschlüsselung im Speicher, MIME aus den Bytes:
Python
class FetchCompanyDocumentsView(LoginRequiredMixin, View):
def get(self, request, path):
full_path = os.path.normpath(
os.path.join(settings.MEDIA_ROOT, path)
)
# Path traversal protection
if not full_path.startswith(settings.MEDIA_ROOT):
raise SuspiciousFileOperation("Invalid path.")
try:
key = get_fernet_key(settings.ENCRYPTION_SECRET)
except AttributeError:
if request.user.is_staff:
return HttpResponse("Encryption key not configured.", status=503)
return HttpResponse("Please try again later.", status=503)
encrypted = open(full_path, "rb").read()
decrypted = Fernet(key).decrypt(encrypted)
# Sniff MIME from decrypted content, not extension
mime = magic.from_buffer(decrypted, mime=True)
return HttpResponse(decrypted, content_type=mime)DAS ERGEBNIS
Was das Audit zu sehen bekam
Für die Menschen auf der Plattform hat sich nichts geändert: dieselben Links, dieselben Dateinamen, dieselbe Geschwindigkeit. Geändert hat sich, was auf der Festplatte liegt, und was das Team dem Auditor vorlegen konnte. Die Frage, was passiert, wenn jemand die Festplatte liest, hat jetzt eine kurze Antwort, Chiffrat, und eine längere, die auf eine Seite passt, weil der ganze Mechanismus in einer Django-View und ihren Settings steckt.
Was sich dem Audit zeigen ließ
- Algorithmus: Fernet, AES-128-CBC mit HMAC-SHA256-Authentifizierung, ein zufälliger IV pro Datei
- Schlüsselableitung: PBKDF2-HMAC-SHA256, 100.000 Iterationen, aus einem Master-Secret, das nur in der Umgebung liegt
- Schlüsselrotation: ein Management-Command verschlüsselt Datei für Datei neu, ohne Downtime und ohne Deployment
- Zugriffspfad: eine login-geschützte View, Pfad normalisiert und auf MEDIA_ROOT begrenzt, Entschlüsselung nur im Speicher
- Zugriffs-Logging: jede Dokumentanfrage wird protokolliert
- Bilanz: über 10.000 ausgelieferte Dokumente, keine Entschlüsselungsfehler, unter 15 ms zusätzlich pro Download
KUNDENSTIMME
Als die Rechtsabteilung einen Nachweis für den Schutz ruhender Daten wollte, haben wir ihr noch am selben Nachmittag die View und die Schlüsselparameter geschickt. Im Produkt hat niemand die Änderung bemerkt; Downloads sehen aus und verhalten sich exakt wie vorher.
CTO
B2B-Marktplatz, Enterprise-Plattform
FÜR DEIN PROJEKT
- Wann das passt
Du speicherst Dokumente, die kein Fremder lesen darf, auf einem Server oder in Backups, an die andere herankommen, und ein Auditor könnte fragen, wie sie geschützt sind. Verschlüsselung in der Anwendung deckt Festplatte, Backup und die verirrte Kopie in einem Zug ab.
- Was du prüfen solltest
Wo Uploads heute liegen und wer diesen Pfad lesen kann: Deploy-User, Backup-Jobs, CI. Ob deine Downloads den Content-Type aus der Dateiendung ableiten; nach der Verschlüsselung geht das nicht mehr. Und wo ein Schlüssel liegen könnte, der weder Datenbank noch Repository ist.
- Was es braucht
Änderungen im Upload- und Download-Pfad, eine Umgebungsvariable oder ein Secret-Manager für den Master-Schlüssel, ein Rotations-Command und ein einmaliger Durchlauf über die Dateien, die schon auf der Festplatte liegen. Schreib die Parameter dorthin, wo die Rechtsabteilung sie findet; das ist die Audit-Antwort.
FAQ
TECHNOLOGIE-STACK
Django
Python
PostgreSQL
Manuel Kasbarian - Geschäftsführer, SophistiXSeit mittlerweile 10 Jahren arbeiten wir immer wieder gerne mit Daniel zusammen. Wir schätzen dabei sehr seine schnellen Reaktionszeiten rund um die Uhr und sein Allroundwissen. Egal ob Serverkonfigurationen oder Programmierungen, er hat immer die passende Lösung.
Schließe dich Manuel an und starte dein nächstes Projekt mit Zuversicht.
Frei für neue ProjekteSchreibe mir