Ein Einmal-Link für Geheimnisse ist eine URL, die ihren Inhalt genau einmal anzeigt und danach dauerhaft wirkungslos ist. Der Browser des Absenders verschlüsselt das Geheimnis mit AES-GCM-256, der Server speichert nur den Geheimtext, und der Schlüssel steht im Teil des Links nach #k=, den Browser nie mitsenden. Das erste Öffnen durch den Empfänger entschlüsselt das Geheimnis und löscht die gespeicherte Kopie im selben Vorgang.
Mit dem kostenlosen Secret-Link-Generator erstellen Sie einen solchen Link ohne Konto.
Der Mechanismus ist einfach. Interessant wird es bei den Sonderfällen, denn dort lassen schlampige Umsetzungen unbemerkt Daten durch.
Der grundlegende Ablauf
- Der Absender fügt das Geheimnis ein. Der Browser erzeugt einen Zufallsschlüssel, verschlüsselt den Inhalt über die Web Crypto API mit AES-GCM-256 und sendet nur den Geheimtext. Der Server speichert ihn unter einem zufälligen, schwer zu erratenden Kürzel und gibt eine URL zurück. Der Browser hängt den Schlüssel als
#k=-Fragment an, das das Gerät des Absenders nie verlässt. - Der Absender teilt die URL über einen beliebigen Kanal: E-Mail, Chat, SMS.
- Der Empfänger öffnet die URL. Ein einfacher
GETliefert nur den Status. Klickt er auf „Anzeigen“, markiert der Server das Geheimnis als verbraucht und gibt in demselben, unteilbaren Schritt den Geheimtext zurück; der Browser des Empfängers entschlüsselt ihn mit dem Schlüssel aus dem Teil des Links nach dem #. - Jeder weitere Besuch bekommt statt des Inhalts den Hinweis, dass der Inhalt gelöscht ist.
Entscheidend ist Schritt 3. Laufen das Lesen und die Statusänderung als zwei getrennte Abfragen (erst SELECT zum Prüfen, dann UPDATE zum Markieren), können zwei gleichzeitige Besucher die Prüfung bestehen, bevor einer von beiden schreibt. Beide erhalten den Inhalt. Das ist ein klassischer TOCTOU-Fehler (time-of-check to time-of-use), und er macht aus der Zusage „genau einmal“ ein „meistens einmal“.
Warum müssen Lesen und Löschen ein einziger Schritt sein?
Weil sonst zwei gleichzeitige Besucher die Prüfung bestehen können, bevor einer von beiden das Geheimnis als verbraucht markiert. Die Lösung: Prüfen, Markieren und Lesen werden zu einer einzigen Datenbankoperation zusammengefasst. In Postgres sieht das mit UPDATE ... RETURNING so aus:
UPDATE secrets
SET burned_at = now()
WHERE slug = $1
AND burned_at IS NULL
AND expires_at > now()
RETURNING payload;
Die Zeilensperre, die das UPDATE setzt, sorgt dafür, dass gleichzeitige Aufrufe nacheinander abgearbeitet werden. Der erste erfüllt die WHERE-Bedingung, setzt burned_at und erhält den Inhalt über RETURNING zurück. Jeder spätere Aufruf findet burned_at bereits gesetzt, trifft null Zeilen und bekommt den Status „gelöscht“. Zwischen „prüfen“ und „verbrauchen“ gibt es kein Zeitfenster, weil beides dieselbe Anweisung ist.
Genau deshalb erfasst LinkPilot jeden Aufruf über eine Datenbankfunktion in einem einzigen, unteilbaren Schritt: Gleichzeitige Versuche, das Geheimnis zu öffnen, können es nicht zweimal preisgeben. Wenn Sie ein Tool prüfen, ist das eine berechtigte Frage an den Anbieter: Geschehen Lesen und Löschen in einem einzigen, unteilbaren Schritt?
Warum liefern gelöschte Links HTTP 410 und nicht 404?
Weil 410 Gone besagt, dass die Ressource existiert hat und dauerhaft nicht mehr verfügbar ist. Genau das ist ein gelöschtes Geheimnis. Ist es verbraucht, soll der Link deutlich und dauerhaft scheitern. LinkPilot tut beim Löschen zwei Dinge:
- Der gespeicherte Inhalt wird im selben Schreibvorgang, der den Status umstellt, mit
[REDACTED]überschrieben. Selbst eine spätere Kompromittierung der Datenbank kann ein verbrauchtes Geheimnis also nicht wiederherstellen. - Weitere Versuche, es zu öffnen, liefern HTTP 410 Gone mit einer klaren Seite „bereits angesehen und vernichtet“.
410 ist der richtige Statuscode: Er bedeutet, dass die Ressource existiert hat und absichtlich und dauerhaft nicht mehr verfügbar ist. Ein 404 dagegen sagt „nie davon gehört“. Der Unterschied zählt auch für Menschen. Ein Empfänger, der „dieses Geheimnis wurde bereits angesehen“ liest, weiß, dass er dem Absender Bescheid geben muss. Der kann im Audit-Verlauf nachsehen und die Zugangsdaten erneuern, falls nicht der Empfänger selbst es geöffnet hat.
Schwer zu erratende Kürzel und warum sie nicht reichen
Zwischen dem öffentlichen Internet und einem ungelesenen Geheimtext steht nur das Kürzel (der Schlüssel steht im Teil des Links nach dem #, nicht auf dem Server). Es muss daher echte Entropie tragen. Ein Kürzel aus einer kryptografisch sicheren Zufallsquelle mit beispielsweise 22 Zeichen in base62 trägt etwa 128 Bit. Durchprobieren ist damit bei keiner Anfragerate, die ein Server überstehen würde, ein realistischer Angriff.
Entropie im Kürzel schützt aber nur gegen Erraten. Gegen eine mitgelesene URL hilft sie nicht: Browserverlauf, Proxy-Logs, weitergeleitete E-Mails, Bildschirmfreigaben, Zwischenablage-Manager. Deshalb ergänzen ernsthafte Umsetzungen weitere Schutzschichten:
- Ablauf, damit eine mitgelesene URL planmäßig ungültig wird.
- Eine Passphrase, damit eine mitgelesene URL allein nicht genügt.
- Löschen nach dem Lesen, damit eine mitgelesene URL nach dem rechtmäßigen Öffnen wertlos ist.
Linkvorschauen und Bots
Chat-Programme (Slack, Teams, iMessage) und E-Mail-Sicherheitsscanner rufen URLs mit einem GET ab, sobald eine Nachricht zugestellt wird, um eine Vorschau zu erzeugen. Löscht Ihr Endpunkt zum Anzeigen schon beim GET, vernichtet der Vorschau-Bot das Geheimnis, bevor ein Mensch es je sieht. Das ist in der Praxis der häufigste Fehler naiver Tools mit Löschen nach dem Lesen.
So schützt LinkPilot dagegen:
- Getrennte Aktionen für Metadaten und Anzeige. Ein
GETauf den Endpunkt zum Anzeigen liefert nur den Status und die Angabe, ob eine Passphrase nötig ist. Ein Aufruf wird erst bei einem ausdrücklichenPOSTgezählt, also bei der Aktion, die ein Mensch mit dem Klick auf die Schaltfläche „Anzeigen“ auslöst. - Passphrase-Sperre (Pro). Ist eine Passphrase gesetzt, erhält niemand den Inhalt ohne den richtigen Hash, weder Bot noch Mensch.
Cache-Control: no-storebei jeder Antwort mit Inhalt, damit Zwischenstationen geheime Inhalte nie zwischenspeichern.
Die Seite zur Sicherheitsarchitektur von LinkPilot dokumentiert diese Behandlung von Anfang bis Ende.
Der Lebenszyklus, Zustand für Zustand
| Zustand | Ausgelöst durch | Was ein Besucher sieht | Inhalt wiederherstellbar? |
|---|---|---|---|
| „Active“ (aktiv) | Erstellung | Seite zum Anzeigen (mit Abfrage der Passphrase, falls gesetzt) | Ja, einmal anzeigbar |
| „Burned“ (gelöscht) | Grenze der Aufrufe erreicht | „Bereits angesehen und vernichtet“, HTTP 410 | Nein, Inhalt überschrieben |
| „Expired“ (abgelaufen) | Ablaufzeitpunkt überschritten | Statusseite für abgelaufene Links | Nein, beim nächsten Zugriff geschwärzt |
| „Revoked“ (widerrufen) | Absender beendet ihn im Dashboard | Anzeige verweigert, HTTP 410 | Nein, sofort und dauerhaft |
Jeder Übergang landet im Audit-Verlauf, einer Pro-Funktion von LinkPilot. Die Ereignistypen sind created, viewed, failed_passphrase, burned, expired und revoked, jeweils mit Zeitstempel und gehashter IP (SHA-256 mit täglich wechselndem Salt; die rohe IP wird nie gespeichert). Dieser Verlauf macht aus „Ich glaube, sie haben es bekommen“ ein „um 10:05 aus dem erwarteten Land angesehen, ein Aufruf, dann gelöscht“.
Übliche Erweiterungen
- Passphrase-Sperre: Vor dem Anzeigen ist eine Passphrase nötig, die über einen anderen Kanal kommt. Eine geleakte URL allein ist dann kein geleaktes Geheimnis. Bei LinkPilot ist das eine Pro-Funktion (29 $ im Monat), zusammen mit frei wählbarem Ablauf und dem Audit-Verlauf; der Generator ohne Konto kann keine Passphrase setzen. Begrenzen Sie die Zahl der Versuche: LinkPilot erlaubt 5 pro IP, Minute und Geheimnis und protokolliert jeden Fehlversuch.
- Zeitlicher Ablauf: Das Geheimnis verfällt nach einem festgelegten Zeitraum, auch wenn es nie gelesen wurde. Das Formular nach der Anmeldung bietet in jedem Tarif 1 Stunde, 24 Stunden oder 7 Tage, bei Pro auch ein frei wählbares Datum mit Uhrzeit; der Generator ohne Konto setzt standardmäßig 7 Tage. Das deckt den Fall ab, dass ein Link im Posteingang vergessen wird, den Löschen nach dem Lesen nicht abdeckt.
- Manueller Widerruf: Der Absender kann ein ungelesenes Geheimnis im Dashboard beenden, sobald ihm etwas merkwürdig vorkommt. Eine günstige Absicherung für den Fall „an den falschen Alex geschickt“.
Sonderfälle, an denen sich gute Umsetzungen zeigen
Die falsche Person öffnet ihn zuerst
Löschen nach dem Lesen hält die falsche Person nicht auf, macht die Fehlzustellung aber sichtbar. Der eigentliche Empfänger landet auf dem Hinweis, dass der Inhalt gelöscht ist, der Audit-Verlauf zeigt einen unerwarteten Aufruf, und Sie erneuern die Zugangsdaten. Das ist ärgerlich, aber begrenzt: genau eine Offenlegung, schnell entdeckt. Ein in den Chat kopiertes Geheimnis dagegen würden Sie nie bemerken.
Das Screenshot-Problem
Einmalig heißt einmal anzeigen, nicht einmal kopieren. Der Empfänger kann das Geheimnis per Screenshot festhalten, kopieren oder abtippen. Der Link verhindert nichts davon, und Anbieter, die anderes andeuten, verkaufen Fiktion. Ehrlich gesagt: Löschen nach dem Lesen kontrolliert den Übertragungsweg, nicht den Empfänger. Vertrauen in den Empfänger ist eine Voraussetzung, kein Ergebnis.
Prefetch im Browser und spekulatives Laden
Manche Browser und Apps laden URLs vorab, die wahrscheinlich angeklickt werden. Weil ein gut gebautes Tool nur bei einem ausdrücklichen POST löscht, verhält sich Prefetch genau wie ein Vorschau-Bot: Er kann das Seitengerüst laden, nie den Inhalt.
Wann Sie keinen Einmal-Link verwenden sollten
- Der Wert wird wieder gebraucht. Wiederherstellbarkeit ist das zentrale Ziel eines Passwortmanagers und genau das, was ein Einmal-Link verhindern soll. Nutzen Sie einen Eintrag im Tresor.
- Mehrere Empfänger brauchen ihn. Ein Link, ein Lesevorgang. Bei mehreren Personen legen Sie entweder einen Link pro Person an (in Ordnung und nachvollziehbar) oder Sie brauchen in Wahrheit eine gemeinsame Zugriffskontrolle.
- Eine Maschine ist der Abnehmer. Dienste und CI sollten aus einem Secrets-Manager lesen, nicht Links anklicken.
Wenn Sie den Mechanismus in Aktion sehen wollen, erstellen Sie einen Link mit dem kostenlosen Secret-Link-Generator: ohne Konto, in Ihrem Browser verschlüsselt, nach dem ersten Öffnen gelöscht.
Weiterlesen
- Passwörter sicher teilen: 5 Methoden im Vergleich
- One-Time Secret, Einmal-Link und Secret-Link: die kurzen Seiten mit dem Tool ganz oben
- Ist OneTimeSecret sicher?
- Die besten Alternativen zu OneTimeSecret
- OneTimeSecret vs Privnote vs Password Pusher
- Ablaufende Links: Das Geheimnis wird gelöscht, nicht nur umgeleitet
- Kostenloser Secret-Link-Generator
Quellen
- W3C, Web Cryptography API.
- IETF, RFC 3986, Abschnitt 3.5 Fragment.
- IETF, RFC 9110: HTTP Semantics, Abschnitt 15.5.11 410 Gone.
- PostgreSQL-Dokumentation, UPDATE.
