Stop Pasting Passwords Into Chat: One-Time Secret Links
A password pasted into a Slack thread does not stay in that thread. It lands in message history, gets pulled into search indexes, sits in backups, and reappears every time someone forwards the channel or quotes it in a ticket. The credential outlives the moment you needed it, in places nobody is watching.
Where pasted credentials actually end up
Chat and email are append-only by design. When you drop a database password into a DM, you are not making one copy — you are making several: the recipient's client, your own sent folder, the server-side store both clients sync from, the full-text search index, and whatever retention or e-discovery system the organization runs. Ticketing is worse. A secret in a Jira comment or a support reply is now attached to a record that may be exported, audited, or handed to a vendor.
The OWASP Secrets Management Cheat Sheet frames the whole problem as a lifecycle: create, store, rotate, revoke, audit. Pasting into chat skips every step. There is no expiry, no access log, and no way to revoke a copy you cannot see. The secret sits there until someone goes looking — or a breach does the looking for you.
Burn-after-read fixes the retention problem
A one-time link inverts the default. Instead of handing someone the secret, you hand them a URL that resolves to the secret exactly once. The recipient opens it, reads it, and the next request — theirs or anyone else's — gets nothing. There is no history to scroll back through and no copy in a search index, because the payload was never the message. The message was a pointer that consumes itself.
This also gives you a cheap integrity signal. Send a one-time link, and if the recipient says it was already used when they opened it, someone else got there first — treat the secret as compromised and rotate it. A password sitting quietly in an inbox gives you no such signal.
If we don't keep it, it can't show up later. A link that works once and is then gone has almost nothing to leak.
End-to-end, not "zero-knowledge proof"
Burn-after-read solves retention but not trust. A naive implementation still stores your plaintext on the server until it is read. The stronger move is to encrypt in the browser before anything is uploaded, so the server only ever holds ciphertext. That is end-to-end encryption: only the sender and the recipient can read the contents, and the service relaying them cannot.
People often call this "zero-knowledge," meaning the server learns nothing about the plaintext. That is a fair informal use, but it is not a zero-knowledge proof — a distinct cryptographic protocol where one party proves a statement is true without revealing anything beyond its truth. The two share a word and nothing else. What a one-time secret tool does is end-to-end encryption: the server is a blind courier holding an opaque blob.
How the key rides in the URL fragment
The trick that keeps the server blind is where the decryption key lives. A URL fragment — the part after # — is handled entirely by the browser. MDN states it plainly: the fragment is not sent to the server when the URI is requested; it is processed by the client after the resource is retrieved. So a link shaped like this keeps the key client-side:
https://exl.ink/s/AbC123#k=<key>&iv=<iv>On exl.ink, one-time secret generates a fresh 256-bit AES-GCM key in the browser with WebCrypto, encrypts your text locally, and uploads only the ciphertext. AES-GCM is an authenticated mode, so a tampered blob fails to decrypt rather than returning garbage. The key and IV go into the # fragment of the link you copy. When the recipient opens it, their browser reads location.hash, fetches the ciphertext, and decrypts in-page. The server stores a blob it cannot read and serves it once. You can try it at exl.ink/secret.
The pitfalls — and when to just rotate
Fragment-based end-to-end encryption is genuinely useful, but it is not magic, and the failure modes are mostly about the link itself rather than the math:
- The link is the secret. Anyone who sees the full URL, including the part after
#, can decrypt it. Paste the whole link into the same Slack channel you were trying to avoid and you have moved the problem, not solved it. Send the link out-of-band where you can. - Fragments still leak locally. They land in browser history and can end up in clipboard managers or a screen share. They are kept out of HTTP requests and server logs — not out of the recipient's machine.
- You trust the page you load. Encrypting in the browser assumes the served JavaScript is honest. That is a real trust boundary. It is much smaller than "the server keeps your plaintext," but it is not zero.
- Ephemeral means ephemeral. Our tools keep nothing around, are rate-limited, and are best-effort. A one-time secret can expire unread or vanish on a restart. It is for handoffs, not storage — use a password manager or a secrets vault for anything you need to keep.
Sometimes the right answer is not to share the secret at all. If a credential may already have leaked — it sat in a channel for a week, it showed up in a log, you are not sure who saw the link — do not carefully hand off the old value. Rotate it. NIST's SP 800-63B leans on length rather than composition rules, and a 20-character random string or a six-word diceware passphrase both clear a comfortable entropy margin. Generate the new value, distribute it, invalidate the old one. Rotation is the only move that defeats copies you cannot see. You can generate one at exl.ink/pass, entirely in your browser.