Zero-Knowledge-Kurztext: Inhalte schützen, die der Server nie sieht
Ein Dienst, der seinen eigenen Inhalt nicht entschlüsseln kann, hat eine Eigenschaft, die die meisten "Ende-zu-Ende-verschlüsselten" Anbieter nicht haben. Warum das URL-Fragment das entscheidende Detail ist und wie One-View-Garantien sauber umgesetzt werden.
Problem
"Ende-zu-Ende-verschlüsselt" ist als Label im Markt massiv überdehnt. In vielen Produkten bedeutet es: der Schlüssel existiert kurzzeitig beim Anbieter, wird nach Nutzung verworfen, und der Anbieter verspricht, ihn nicht zu kopieren. Das ist ein Vertrauensversprechen, kein struktureller Schutz. Wer in einer Behörde arbeitet und mitteilt, dass eine bestimmte E-Mail-Adresse morgen früh ein Paket bekommen soll, kann dem Anbieter nicht grundsätzlich trauen, nicht weil er bösartig ist, sondern weil Anordnungen, Insider-Zugriffe und erfolgreiche Angriffe existieren.
Zero-Knowledge dreht die Vertrauensannahme um: der Server kann den Inhalt nicht entschlüsseln, weil er den Schlüssel nie gesehen hat, auch nicht kurzzeitig. Für Kurzmitteilungen mit hoher Vertraulichkeit (einmalig zu übertragende Passwörter, Zugangsdaten, Adressen, kurze Nachrichten) ist das der strukturell sauberste Schutz.
Kurze Antwort
Der Entschlüsselungs-Schlüssel entsteht im Browser des Senders als 32-Byte-Zufallswert, wird per crypto.getRandomValues() generiert, verschlüsselt den Inhalt mit AES-256-GCM und landet im URL-Fragment (nach dem #). Der Browser schickt das Fragment nie an den Server. Der Server sieht nur den Ciphertext, den Nonce und das Salt.
Der Empfänger klickt den Link. Sein Browser hält das Fragment lokal, fragt nur die Ciphertext-Zeile beim Server ab, entschlüsselt im Browser und zeigt den Klartext an. Für One-View-Notes wird die Zeile serverseitig beim ersten erfolgreichen Abruf gelöscht. Danach existiert weder Klartext noch Chiffrat mehr.
Tiefgang
Das URL-Fragment als lokales Geheimnis
RFC 3986 definiert den Fragment-Bezeichner (der Teil nach #) explizit als clientseitig auszuwertend. Browser schicken das Fragment nicht im HTTP-Request an den Server, nicht im Referer-Header, nicht in Logs. Es ist die einzige Stelle in einer URL, die dem Server strukturell unzugänglich bleibt.
Das macht das Fragment zum idealen Transportmittel für ein Klartext-Geheimnis, das einen Anhängerseite eines Ciphertexts werden soll. Der Absender erzeugt den Schlüssel, verschlüsselt, speichert nur den Ciphertext beim Anbieter und baut eine URL, die den Schlüssel ins Fragment hängt. Der Link kann in Messenger, E-Mail, Chat weitergereicht werden, jeder, der ihn vollständig erhält, kann entschlüsseln.
Schlüsselerzeugung und Schema
Im Browser, mit Web Crypto API:
const key = crypto.getRandomValues(new Uint8Array(32)); // 256 bit
const nonce = crypto.getRandomValues(new Uint8Array(12)); // 96 bit GCM
const subtleKey = await crypto.subtle.importKey(
'raw', key, { name: 'AES-GCM' }, false, ['encrypt', 'decrypt']
);
const ciphertext = await crypto.subtle.encrypt(
{ name: 'AES-GCM', iv: nonce }, subtleKey, plaintextBytes
);
// Server bekommt: ciphertext || nonce || (optional) salt
// URL wird: https://notes.example.de/n/<id>#k=<base64url(key)>Der Schlüssel geht ins Fragment, alles andere an den Server. GCM liefert Authentizität mit; eine Manipulation am Ciphertext führt zum Entschlüsselungsfehler, nicht zu einem stillen Kippbit.
Passwort als zweite Schicht
Für Fälle, in denen der Link selbst in falsche Hände geraten könnte (mitgeloggter Messenger, durchsichtiger Proxy), lässt sich ein zweiter Schlüssel aus einem Passwort ableiten:
const salt = crypto.getRandomValues(new Uint8Array(16));
const passKey = await scrypt(password, salt, N=2**15, r=8, p=1, dkLen=32);
const finalKey = xor(key, passKey);Der Server speichert das Salt. Zum Entschlüsseln muss der Empfänger das Passwort kennen. Die Brute-Force-Kosten hängen am scrypt-Parameter N; auf modernen Clients dauert eine Iteration einige zehntel Sekunden, was für einen einzelnen Zugriff in Ordnung ist und für Brute-Force massiv teuer.
Die Lösch-Garantie
Zero-Knowledge ist die halbe Miete; die andere Hälfte ist die verlässliche Löschung. Eine saubere Umsetzung:
- One-View-Notes: atomarer DB-Delete
... WHERE id = $1 AND consumed_at IS NULL RETURNING *. Nur eine parallele Leseanfrage gewinnt; alle anderen bekommenNotFound. - Zeitfenster-Notes:
expires_at-Spalte, Cleanup-Job löscht abgelaufene Zeilen. Bis dahin kein Zugriff möglich, danach physisch gelöscht. - Keine Backups der Notes-Tabelle. Das ist wichtig und unbequem: ein Restore würde eine vermeintlich gelöschte Note wieder zugänglich machen. Die Tabelle wird explizit aus dem
pg_dump-Scope herausgenommen, oder separat mit--exclude-table=notesgesichert und der Sicherungsanhang gleichzeitig gelöscht. - Kein Support-Override. Es gibt keine Möglichkeit, einen verlorenen Schlüssel wiederherzustellen. Das ist Produktmerkmal, kein Defekt.
Was der Server sichtbar hat
Metadaten bleiben beim Server unvermeidlich:
- Zeitpunkt des Erstellens und Abrufens.
- IP-Adresse (kurzzeitig, Log-Rotation minimiert das).
- Grösse des Ciphertexts.
- Zugriffsmuster (angelegt, abgerufen, gelöscht).
Wer auch das minimieren will, verzichtet auf IP-Logging (gegen Missbrauch-Monitoring) und löscht Metadaten-Logs früh. Es bleibt kein Verfahren, die Metadaten auf Null zu bekommen, der Server muss wissen, was er löschen soll.
Abgelehnte Alternativen und Mythen
"Wir speichern den Schlüssel kurz am Server und werfen ihn danach weg." Der Schlüssel war am Server. Eine Anordnung an den Anbieter, eine Speicher-Forensik, ein Insider mit Zugriff auf den Prozess, alle drei können den kurzlebigen Schlüssel abgreifen. Zero-Knowledge schliesst das strukturell aus.
"Wir nutzen PGP." Funktional eine sehr starke Lösung, aber operativ komplex: jeder Empfänger braucht einen Schlüssel, Key-Management ist nicht gelöst, Messenger und Webmail-Clients unterstützen PGP unterschiedlich. Zero-Knowledge-Kurztext-Dienste sind niederschwellige Alternativen für Einzelfälle.
"Ein S3-Bucket mit Client-seitigem Krypto." Funktioniert, aber die Lösch-Garantie muss separat gebaut werden. One-View-Semantik im Client durchzusetzen ist fragil (der Client kann mehrfach aufrufen); zuverlässig ist sie nur serverseitig.
"Das Fragment lässt sich per JavaScript abfangen." Nur, wenn auf der Seite selbst feindlicher JavaScript-Code läuft. Deshalb gehören zu Zero-Knowledge-Diensten eine scharfe CSP ohne inline-Scripts (siehe kommender Beitrag dieser Serie) und ein minimal gehaltener Bundle. Kein Drittanbieter-JavaScript, kein CDN, kein Analytics.
Wie Svelnor hier hilft
Svelnor Note implementiert den im Beitrag beschriebenen Flow direkt: Schlüssel-Erzeugung im Browser mit crypto.getRandomValues(), AES-256-GCM-Verschlüsselung, Schlüssel-Transport im URL-Fragment, atomarer One-View-Delete (DELETE ... WHERE id = $1 AND consumed_at IS NULL RETURNING *), kein Support-Override. Optional scrypt-basiertes Passwort-Hardening als zweite Schicht, konfigurierbare TTL für Zeitfenster-Notes, keine Backups der Notes-Tabelle. Metadaten (Erstell-Zeitpunkt, Abruf-Zeitpunkt, Größe des Ciphertexts) bleiben serverseitig sichtbar; wir minimieren Log-Retention, aber sie sind nicht entfernbar.
Verifikation
- RFC 3986 (URI-Syntax), Abschnitt 3.5 zum Fragment.
- Web Crypto API: MDN-Dokumentation.
- Offene Vergleichs-Implementationen: PrivateBin, OneTimeSecret, Bitwarden Send. Jeder Code ist einsehbar, jeder Prüfer kann nachvollziehen, wo der Schlüssel entsteht und wohin er geht.
- Tests: HTTP-Logs auf dem Server auf Suche nach dem Fragment-Wert, er darf nie auftauchen. DevTools-Netzwerk-Tab beim Abruf: nur die
id, nicht der Schlüssel im Request.
Offene Punkte
Browser-Verlauf. Das Fragment steht in der URL des Browser-Verlaufs. Wer Einmal-Notes verwendet, sollte das dem Empfänger bewusst machen, der Verlauf ist eine Kopie des Schlüssels für die Dauer der Browser-Historie.
Mail-Preview. Manche Mail-Dienste holen URLs im Link vor, um Previews zu bauen. Wenn der Preview-Crawler den Link öffnet, wird eine One-View-Note vor dem eigentlichen Empfänger konsumiert. Das ist ein reales Problem und lässt sich nur abmildern (nicht heilen): z.B. mit einem Klick-Warte-Schritt, bei dem der Empfänger aktiv bestätigt, bevor die Note freigegeben wird.
Meta-Daten. Trotz Zero-Knowledge bleiben IP, Zeitpunkte, Abruf-Muster als Residuum. Für hochsensible Fälle braucht es zusätzliche Schichten: Tor-Zugang, bewusste zeitliche Verzögerung, gezieltes Minimierung der Log-Retention.