Geheimnisse teilen ohne Fremdcode: Browser-Kryptografie für Secret Sharing
Sensible Daten im Internet teilen – das ist wirklich ein Albtraum. Du schickst ein Passwort per E-Mail und löschst es dann panisch wieder. Du nutzt einen Drittanbieter und hoffst, dass niemand mitliest. Du schickst es per Slack und weißt, dass dein Unternehmen das für immer archiviert hat.
Es muss doch einen besseren Weg geben.
Gibt es auch – und du kannst ihn komplett im Browser bauen.
Das Problem mit externen Secret-Sharing-Diensten
Dienste wie OneTimeSecret haben das Konzept selbstzerstörender Links populär gemacht. Secret einfügen, Einmal-Link bekommen, teilen, fertig – nach dem Lesen ist alles weg. Aber da gibt es einen Haken, über den viele Nutzer nicht nachdenken: Was passiert eigentlich mit deinen Daten?
Selbst bei „temporären" Diensten landet dein Secret oft als Klartext auf deren Servern. Sie versprechen, nichts zu lesen. Sie versprechen, alles zu löschen. Aber warum Versprechen vertrauen, wenn man die Möglichkeit komplett ausschalten kann?
Hier kommt End-to-End-Verschlüsselung ins Spiel. Bei echter E2EE kann der Server dein Secret nicht lesen. Er speichert nur Ciphertext – nutzlose Zufallsbytes ohne den Schlüssel. Der Server wird zur dummen Speicherbox, nicht zum Hüter deiner Geheimnisse.
So funktioniert E2EE Secret Sharing
Stell dir vor: Bob will Alice ein Datenbank-Passwort schicken. Das Schöne daran: Die Verschlüsselung passiert komplett in Bobs Browser, bevor überhaupt etwas zum Server übertragen wird.
Der Ablauf:
- Bob gibt sein Secret ein und wählt eine Passphrase
- Sein Browser leitet einen kryptografischen Schlüssel aus dieser Passphrase ab
- Das Secret wird lokal mit diesem Schlüssel verschlüsselt
- Bob schickt Alice einen Link (mit Ciphertext und Salt) plus die Passphrase (über einen anderen Kanal)
Warum getrennte Kanäle? Wenn Link und Passphrase zusammen reisen und jemand den Link abfängt, hat er alles. Schick den Link über Slack, die Passphrase über Signal. Oder SMS. Oder Brieftaube. Der Punkt ist: zwei unterschiedliche Angriffsoberflächen.
Alice bekommt den Link, gibt ihre Passphrase ein, ihr Browser leitet den Schlüssel erneut ab, und das Secret wird entschlüsselt – alles ohne dass der Server jemals lesbare Daten zu sehen bekommt.
Die Kryptografie dahinter
Jetzt wird es technisch, aber bleib dran – es ist elegant.
Schlüsselableitung: PBKDF2
Menschen sind schrecklich darin, zufällige, hochentropische Schlüssel zu generieren. Wir tippen „Passwort123" und fühlen uns kreativ. Also brauchen wir eine Funktion, die schwache Passphrasen in starke kryptografische Schlüssel verwandelt – langsam, absichtlich.
PBKDF2 (Password-Based Key Derivation Function 2) macht genau das. Es nimmt deine Passphrase, fügt ein zufälliges Salt hinzu (öffentlich gespeichert) und führt sie durch 600.000 Iterationen von SHA-256-Hashing. Jeder Versuch bei einem Brute-Force-Angriff kostet CPU-Zeit. Bei 600.000 Iterationen wird das Knacken einer mittelmäßigen Passphrase rechnerisch unmöglich.
async function deriveKey(passphrase, salt) {
const keyMaterial = await crypto.subtle.importKey(
"raw",
new TextEncoder().encode(passphrase),
"PBKDF2",
false,
["deriveKey"]
);
return crypto.subtle.deriveKey(
{
name: "PBKDF2",
salt: salt,
iterations: 600_000,
hash: "SHA-256"
},
keyMaterial,
{ name: "AES-GCM", length: 256 },
false,
["encrypt", "decrypt"]
);
}
Symmetrische Verschlüsselung: AES-GCM
Für die eigentliche Verschlüsselung nutzen wir AES-256-GCM. Das ist nicht das AES, das du vielleicht aus theoretischen Zusammenhängen kennst – es ist die authentifizierte Variante. Der GCM-Modus verschlüsselt nicht nur deine Daten, sondern erzeugt auch einen Authentifizierungstag. Wenn jemand den Ciphertext auf dem Transportweg manipuliert, schlägt die Entschlüsselung mit einem Fehler fehl – statt stillschweigend Müll zu produzieren.
Außerdem braucht AES-GCM einen einzigartigen Initialisierungsvektor (IV) für jede Verschlüsselung. Stell ihn dir wie das Salt vor: öffentlich, zufällig, aber entscheidend für die Sicherheit. AES-GCM ist solide – die NSA empfiehlt es für klassifizierte Informationen.
async function encrypt(plaintext, key) {
const iv = crypto.getRandomValues(new Uint8Array(12));
const encoded = new TextEncoder().encode(plaintext);
const ciphertext = await crypto.subtle.encrypt(
{ name: "AES-GCM", iv: iv },
key,
encoded
);
return { iv, ciphertext };
}
Warum Null Abhängigkeiten zählt
Dieser Ansatz ist radikal: keine npm-Pakete. Keine Crypto-Bibliotheken. Kein Vertrauen in Drittanbieter-Code.
Du nutzt das, was bereits in deinem Browser steckt. Die Web Crypto API ist seit 2015 standardisiert und in jedem modernen Browser integriert. Sie wird von den Plattform-Anbietern unterstützt – Apple, Google, Microsoft, Mozilla –, die massiv in korrekte, constant-time Implementierungen investiert haben.
Betrachte die Angriffsoberfläche typischer abhängigkeitsreicher Anwendungen. Log4Shell passierte wegen einer Abhängigkeit. Der left-pad-Vorfall legte das Internet lahm, weil es eine Abhängigkeit gab. Jedes npm install ist eine Vertrauensentscheidung.
Mit der Web Crypto API vertraust du der nativen Browser-Implementierung. Das ist eine viel kleinere Angriffsoberfläche als das Vertrauen in irgendein npm-Paket mit zwei Millionen Downloads pro Woche.
Serverseitig: Dummer Speicher ist guter Speicher
Auf dem Backend speicherst du:
- Den Ciphertext (nutzlos ohne Schlüssel)
- Das Salt (nicht geheim, wird für Schlüsselableitung gebraucht)
- Den IV (nicht geheim, wird für Entschlüsselung gebraucht)
- Metadaten (Erstellungszeitpunkt, Ablauf, Aufrufzahl)
Konfiguriere Redis (oder Valkey) mit save "" und appendonly no – also keine Persistenz whatsoever. Wenn der Prozess stirbt, verdunstet alles. Richte Swap-Configuration richtig ein, damit keine Daten auf die Festplatte gelangen.
Der Server wird zum temporären verschlüsselten Blob-Speicher. Er setzt Zugriffsregeln durch (Einmalansicht, Ablauf), kann aber den Inhalt nicht lesen – selbst wenn er wollte.
Was das für deinen Stack bedeutet
Du kannst das direkt in jede Webanwendung einbetten. Ein Formularfeld hier, ein paar JavaScript-Funktionen dort, und plötzlich hat deine App sicheres, ephemeres Secret-Sharing eingebaut. Keine Backend-Änderungen nötig – dein Server speichert und serviert nur verschlüsselte Blobs.
Das ist besonders stark für:
- Interne Tools, die temporär Credentials teilen müssen
- Customer-Support-Systeme, die sensible Daten übertragen
- Developer-Workflows zum Teilen von API-Keys oder Datenbank-Passwörtern
Die Secrets sind kryptografisch von deiner Infrastruktur isoliert. Selbst wenn jemand deine Datenbank kompromittiert, bekommt er Ciphertext, den er ohne Passphrase nicht knacken kann.
Die Abwägungen lohnen sich
Ist PBKDF2 mit 600.000 Iterationen langsamer als Argon2? Ja. Argon2, der moderne Goldstandard für Passwort-Hashing, ist in der Web Crypto API noch nicht verfügbar. Aber bedenke den Kontext: Das sind Einmal-Secrets, die meist innerhalb von Minuten angeschaut werden. Die Rechenkosten sind akzeptabel.
Der Kompromiss – leicht schwächerer KDF für null Abhängigkeiten – lohnt sich für die meisten Anwendungsfälle. Du schützt keine Nuklearcodes. Du teilst ein Datenbank-Passwort, das morgen ohnehin geändert wird.
Bau es selbst, vertrau keinem Dienst
Das Schöne an E2EE ist, dass du dem Dienstanbieter nicht vertrauen musst. Du kannst den Code prüfen. Du kannst ihn selbst hosten. Du kannst verifizieren, dass der Server wirklich niemals Klartext sieht.
In einer Welt, in der Datenschutzverletzungen tägliche Nachrichten sind, sind Systeme, in denen der Server kryptografisch blind ist, nicht nur clever – sie sind verantwortungsvoll.
Also, das nächste Mal wenn du ein Secret online teilen musst: Denk daran, dass der beste Schutz für Daten darin besteht, sie niemals unverschlüsselt aus den Händen des Nutzers zu geben. Und mit der Web Crypto API hast du die Werkzeuge dafür, direkt im Browser.
Bereit für die Implementierung? Die Web Crypto API funktioniert in allen modernen Browsern. Keine Installation, kein Build-Schritt, keine Abhängigkeiten – nur nativ browserbasierte Kryptografie, die darauf wartet, benutzt zu werden.