Leistungen

Backend-Entwicklung, Security Engineering

Branche

B2B-Marktplatz

Jahr

2024

Dokumente transparent verschlüsseln mit Django und Fernet.

Ein B2B-Marktplatz verarbeitete täglich Tausende sensibler Dokumente: NDAs, Jahresabschlüsse, Unternehmensbewertungen und proprietäre Verträge. Alles gespeichert im Klartext auf der Festplatte. Ein Servereinbruch hätte vertrauliche Daten jedes Kunden auf der Plattform offengelegt. Sie brauchten eine Verschlüsselung im Ruhezustand, die für Nutzer transparent, für Admins verwaltbar und für DSGVO-Audits nachweisbar war.

Jedes NDA, jeder Jahresabschluss und jeder proprietäre Vertrag lag als gewöhnliche Datei auf der Festplatte. Ein einziger Server-Einbruch hätte alles offengelegt.

01.
DIE HERAUSFORDERUNG

Klartextdateien auf einem gemeinsamen Server

Die Plattform speicherte hochgeladene Dokumente als reguläre Dateien in Djangos MEDIA_ROOT. Jeder Angreifer mit Lesezugriff auf das Dateisystem - durch eine kompromittierte Abhängigkeit, ein geleaktes SSH-Schlüsselpaar oder ein falsch konfiguriertes Backup - hätte sofortigen Zugriff auf jedes NDA und Finanzdokument auf der Plattform gehabt. Das Rechts-Team des Kunden stufte dies als kritische DSGVO-Haftung ein: personenbezogene und geschäftliche Daten unverschlüsselt gespeichert, ohne Zugriffs-Logging und ohne Möglichkeit, den Schutz ruhender Daten bei einem regulatorischen Audit nachzuweisen.

Vorher vs. Nachher

Dasselbe Verzeichnis auf dem Server - vor und nach dem Deployment der Verschlüsselungsschicht.

Klartext
Verschlüsselt
KlartextVerschlüsselt
02.
DIE LÖSUNG

Fernet mit On-the-Fly-Entschlüsselung

Ich habe eine symmetrische Verschlüsselungsschicht mit Fernet implementiert, das AES-128-CBC mit HMAC-SHA256-Authentifizierung kombiniert. Jede Datei wird vor dem Schreiben auf die Festplatte verschlüsselt, wobei ein Schlüssel verwendet wird, der aus einem Master-Secret über PBKDF2-HMAC mit 100.000 Iterationen abgeleitet wird. Der Schlüssel berührt nie die Datenbank - er existiert ausschließlich in einer Umgebungsvariable und kann rotiert werden, ohne Code neu zu deployen. Wenn ein Benutzer ein Dokument anfordert, entschlüsselt die View es im Arbeitsspeicher nur für die Dauer der HTTP-Antwort. Die Datei auf der Festplatte bleibt jederzeit verschlüsselt. Nach der Entschlüsselung erkennt python-magic den tatsächlichen MIME-Typ aus dem binären Inhalt, nicht aus der Dateiendung, da verschlüsselte Dateien keine aussagekräftige Endung haben. Path-Traversal-Schutz stellt sicher, dass nur Dateien innerhalb von MEDIA_ROOT ausgeliefert werden können, mittels os.path.normpath und einem Prefix-Check.

Dokumente werden mit Fernet verschlüsselt, bevor sie auf die Festplatte geschrieben werden. Der Schlüssel wird über PBKDF2 abgeleitet:

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)

Die Verschlüsselungs-Pipeline

Bewege den Mauszeiger über die Knotenpunkte, um den Weg eines Dokuments vom Upload bis zur sicheren Auslieferung nachzuverfolgen.

Upload

Nutzer lädt ein vertrauliches Dokument über die Django-Anwendung hoch.

PBKDF2

Ein kryptografischer Schlüssel wird aus dem Master-Secret über PBKDF2-HMAC mit 100.000 Iterationen abgeleitet.

Fernet

Die Dokumentbytes werden mit Fernet (AES-128-CBC + HMAC-SHA256) verschlüsselt und auf die Festplatte geschrieben.

Anfrage

Ein autorisierter Nutzer fordert das Dokument an. Die View prüft die Berechtigung und entschlüsselt nur im Arbeitsspeicher.

MIME-Sniff

python-magic erkennt den tatsächlichen MIME-Typ aus den entschlüsselten Bytes, nicht der Dateiendung, für korrekte Auslieferung.

  1. 01UploadNutzer lädt ein vertrauliches Dokument über die Django-Anwendung hoch.
  2. 02PBKDF2Ein kryptografischer Schlüssel wird aus dem Master-Secret über PBKDF2-HMAC mit 100.000 Iterationen abgeleitet.
  3. 03FernetDie Dokumentbytes werden mit Fernet (AES-128-CBC + HMAC-SHA256) verschlüsselt und auf die Festplatte geschrieben.
  4. 04AnfrageEin autorisierter Nutzer fordert das Dokument an. Die View prüft die Berechtigung und entschlüsselt nur im Arbeitsspeicher.
  5. 05MIME-Sniffpython-magic erkennt den tatsächlichen MIME-Typ aus den entschlüsselten Bytes, nicht der Dateiendung, für korrekte Auslieferung.

Die View entschlüsselt im Arbeitsspeicher, erkennt den echten MIME-Typ und dient mit Path-Traversal-Schutz aus:

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)
03.
DAS ERGEBNIS

Unsichtbare Sicherheit, auditierbare Compliance

Die Verschlüsselungsschicht war für Endnutzer unsichtbar - Dokumente wurden wie zuvor heruntergeladen, mit der gleichen Geschwindigkeit und den gleichen Dateinamen. Hinter den Kulissen war jede Datei nun mit authentifizierter Verschlüsselung im Ruhezustand geschützt, was das Klartextrisiko beseitigte. Als das DSGVO-Audit kam, konnte das Team Schlüsselableitungsparameter, Verschlüsselungsalgorithmus, Schlüsselrotationsfähigkeit und Zugriffs-Logging nachweisen - alles aus einer einzigen Django-View. Das System verarbeitete über 10.000 Dokumente ohne einen einzigen Entschlüsselungsfehler. Administratoren erhielten spezifische Fehlermeldungen, wenn der Verschlüsselungsschlüssel nicht in der Umgebung vorhanden war, während normale Benutzer eine freundliche Wiederversuchen-Nachricht sahen.

AES-128 + HMAC-SHA256
100K PBKDF2-Runden
< 15 ms Latenz
0 Fehler / 10.000+ Docs
WAS DER KUNDE SAGT

"Wir schlafen besser, seit wir wissen, dass jedes Dokument verschlüsselt ist. Als unser Rechts-Team einen Nachweis für den Schutz ruhender Daten brauchte, hatten wir ihn in fünf Minuten parat, nicht in fünf Wochen."

CTO

B2B-Marktplatz · Enterprise-Plattform

FAQ

Warum Fernet statt AES-GCM oder andere Modi?

Wie wird der Verschlüsselungsschlüssel verwaltet und rotiert?

Was passiert, wenn ein Admin vergisst, den Verschlüsselungsschlüssel zu setzen?

TECHNOLOGIE-STACK