Was ist das?

Ein Service zum einmaligen Teilen von Geheimnissen — Passwörter, API-Keys, private Nachrichten. Statt Secrets im Klartext per Chat oder E-Mail zu verschicken, erzeugt Once einen Link, der genau einmal geöffnet werden kann. Danach ist das Geheimnis unwiderruflich gelöscht.

Das Besondere: Die Ende-zu-Ende-Verschlüsselung stellt sicher, dass der Server zu keinem Zeitpunkt Zugriff auf den Klartext hat. Der Verschlüsselungsschlüssel steckt im URL-Fragment (#key) und wird nie an den Server gesendet.

Wie es funktioniert

  1. CLI verschlüsselt das Secret lokal mit einem zufälligen AES-256-GCM -Schlüssel
  2. Server speichert nur den Ciphertext mit einer öffentlichen ID
  3. Link wird erzeugt: once.mathis-adler.dev/s/<id>#<key> — der Schlüssel im Fragment verlässt nie den Client
  4. Empfänger öffnet den Link, klickt auf “Anzeigen”, der Browser holt den Ciphertext und entschlüsselt ihn per WebCrypto
  5. Secret wird konsumiert atomar beim ersten Abruf, danach 410 Gone

Sicherheitsentscheidungen

  • Zero-Knowledge-Architektur — Der Server sieht nie den Schlüssel oder Klartext. Selbst bei einer Server-Kompromittierung sind die Secrets geschützt.
  • POST statt GET zum Konsumieren — Unfurl-Bots in Slack, Teams oder Discord machen nur GET-Requests. Die Reveal-Seite (GET /s/:id) zeigt nur den “Anzeigen”-Button, ohne das Secret zu konsumieren. Erst der explizite POST /api/secrets/:id/consume gibt den Ciphertext zurück.
  • Atomarer Consume — SQLite’s UPDATE ... RETURNING stellt sicher, dass bei gleichzeitigen Requests nur der erste den Ciphertext erhält.
  • Content Security Policy script-src 'self' verhindert, dass eingeschleuster Code den Schlüssel aus dem URL-Fragment exfiltrieren kann.
  • 128-Bit IDs — 22-Zeichen Base64url (~2^128 Kombinationen) machen Brute-Force-Angriffe auf IDs unmöglich.
  • Zweistufiges Rate Limiting (Nginx + Express) — verschärft für Consume-Requests und ungültige IDs.
  • Automatische Bereinigung — Abgelaufene Secrets werden sofort gelöscht, konsumierte nach einer Stunde Grace Period.

Was ich gelernt habe

  • URL-Fragmente (#...) werden vom Browser nie an den Server gesendet — das macht sie ideal für clientseitige Schlüssel. Das ist kein Hack, sondern Teil der HTTP-Spezifikation (RFC 3986).
  • Die WebCrypto API ist mächtig, aber ihre Ergonomie erfordert Sorgfalt: Key-Import, IV-Handling und die korrekte Trennung von IV, Ciphertext und Auth-Tag müssen exakt stimmen.
  • “Einmal lesen, dann löschen” klingt einfach, aber die Atomarität ist entscheidend. Ohne UPDATE ... RETURNING wäre ein Race-Condition-Window, in dem zwei Requests gleichzeitig den Ciphertext erhalten.

Glossar

Ende-zu-Ende-Verschlüsselung
Ein Kommunikationsprinzip, bei dem nur Sender und Empfänger die Nachricht lesen können. Der Transportweg — einschließlich des Servers — sieht nur verschlüsselte Daten und hat keinen Zugriff auf den Schlüssel.
AES-256-GCM
Ein authentifizierter Verschlüsselungsmodus, der AES mit 256-Bit-Schlüssel und Galois/Counter Mode kombiniert. Bietet gleichzeitig Vertraulichkeit und Integritätsschutz — manipulierte Ciphertexte werden bei der Entschlüsselung erkannt.
URL-Fragment
Der Teil einer URL nach dem #-Zeichen. Wird laut HTTP-Spezifikation nie an den Server gesendet und nur clientseitig vom Browser verarbeitet. Eignet sich dadurch als Kanal für Daten, die der Server nicht sehen soll.
Base64url
Eine URL-sichere Variante der Base64-Kodierung, die + durch - und / durch _ ersetzt. Kann ohne URL-Encoding direkt in URLs verwendet werden.
WebCrypto API
Eine Browser-API für kryptografische Operationen wie Verschlüsselung, Hashing und Schlüsselgenerierung. Läuft nativ im Browser ohne externe Libraries und unterstützt unter anderem AES-GCM, RSA und ECDSA.
Atomare Operation
Eine Operation, die entweder vollständig oder gar nicht ausgeführt wird — ohne Zwischenzustände. In Datenbanken verhindert Atomarität, dass parallele Requests inkonsistente Ergebnisse erzeugen.
Content Security Policy
Ein HTTP-Header, der dem Browser vorschreibt, welche Ressourcen eine Seite laden darf. script-src 'self' erlaubt nur Scripts von der eigenen Domain und blockiert damit eingeschleusten Code (XSS).
Rate Limiting
Ein Schutzmechanismus, der die Anzahl der Anfragen pro Zeiteinheit begrenzt. Verhindert Brute-Force-Angriffe und Token-Enumeration durch automatisierte Abfragen.