SikkerLink Security
How SikkerLink keeps a one-time secret link encrypted in your browser, why the server can never read it, and how it self-destructs after one view.
SikkerLink is a free tool for sending a single secret over a link that opens once and then destroys itself. It is a separate product from the SikkerKey vault, and it runs on a different security model: SikkerLink is zero-knowledge. The secret is encrypted in the sender's browser, and the server only ever holds bytes it cannot read.
As with the rest of these docs, every claim here reflects what the code actually does.
The model in one line
The browser encrypts the secret before it leaves the page. The passphrase that decrypts it travels in the link's URL fragment, or is shared out of band, and is never sent to the server. The server stores an opaque blob and hands it back exactly once. We cannot read your secret because we never receive the key.
How SikkerLink secures your secret
Encrypted in your browser
When you create a link, everything cryptographic happens locally before anything is sent:
- A random passphrase is generated in the browser using the platform's cryptographic RNG, 256 bits of entropy. This passphrase is the only thing that can decrypt the secret.
- The encryption key is derived from it with PBKDF2 (250,000 iterations, SHA-256) over a random per-link salt.
- The secret is encrypted with AES-256-GCM under that key with a random 96-bit IV. The GCM authentication tag means any tampering with the stored ciphertext fails decryption.
- Only an opaque blob leaves the browser: the salt, IV, and ciphertext packed together and base64url-encoded. It is meaningless without the passphrase.
The passphrase itself never reaches the server. It rides in the URL fragment (the part after #), which browsers do not transmit in HTTP requests, or you share it out of band and the recipient pastes it in.
The server only holds bytes it can't read
For each link the server stores exactly three things: the opaque ciphertext blob, a SHA-256 hash of the passphrase, and an expiry timestamp. No plaintext, no passphrase, no key. There is no code path that decrypts a SikkerLink secret, because the server never has the material to do it.
The access gate
The recipient must prove they hold the passphrase before the server releases the blob. The browser sends SHA-256(passphrase), and the server compares it, in constant time, against the hash stored at creation. Because the passphrase is high-entropy and generated by us, this hash is safe to store and is not the decryption key: the real key is derived separately via PBKDF2, so knowing the hash tells an attacker nothing about how to decrypt the blob. A wrong passphrase fails this gate and the link is left untouched, so the recipient can try again.
One view, then gone
A correct reveal deletes the link in the same locked database transaction that returns the blob. The read is serialized with a row lock, so two simultaneous reveals cannot both succeed: one wins, deletes, and returns the secret; the other finds the row already gone. After that first successful open, the ciphertext no longer exists on our side, and reloading the page shows nothing.
A link that never existed, one that was already opened, and one that has expired all return the same "no longer available" response, so nothing about which links exist is observable beyond already holding the link.
Expiry
The sender chooses how long the link lives, from one hour to one day. Expiry is checked at read time under the same lock, so an expired link is gone the moment anyone tries it, whether or not the background cleaner has swept it yet. A cleaner also deletes expired rows on a short interval to reclaim storage. There is no way to extend a link; to share again, create a new one.
No account, minimal data
Creating and opening a link require no account and no sign-in. SikkerLink stores no identity for either party, only the blob, its access hash, and its expiry. Reveal pages are marked noindex and excluded from crawling.
Abuse controls
Because creating a link is open and unauthenticated, the create and reveal endpoints are rate-limited per source IP, the stored blob is size-capped, and request bodies are bounded before any handler runs.
In transit
Like the rest of SikkerKey, SikkerLink is served only over TLS 1.2+ with HSTS and a strict Content-Security-Policy that restricts where the page can talk. The shared transport, hosting, and backup details are in the security overview.
What we store vs. never see
| Stored on our side | Never on our side |
|---|---|
| The opaque ciphertext blob (salt + IV + AES-256-GCM ciphertext) | The plaintext secret (encrypted and decrypted only in the browser) |
SHA-256(passphrase) as a one-time access gate | The passphrase (in the link fragment or shared out of band, never sent to us) |
| The expiry timestamp | The AES key (derived from the passphrase in the browser via PBKDF2) |
How this differs from the vault
The vault and SikkerLink solve different problems, so they make different trade-offs:
- The vault is not zero-knowledge, by necessity. Machines and CLIs fetch plaintext secrets continuously, so the server decrypts in memory on demand to serve them. The protection there is envelope encryption, machine authentication, and a decryption root that never touches disk, all covered in the security overview.
- SikkerLink is zero-knowledge, by design. It is a one-time, human-to-human handoff, so the browser can generate and hold the key and the server never needs it. The cost is that a lost passphrase means a lost secret: there is no recovery path, because there is nothing on our side to recover it from.
What's on your side
- The passphrase. If you include it in the link, anyone with that link can open the secret once, so treat the full link as the secret. If you share it separately, the recipient needs both the link and the passphrase.
- Choosing the lifetime. Shorter is safer; pick the shortest window that still lets the recipient open it in time.
- The plaintext once it is revealed. After the recipient opens the link, the bytes are in their hands and out of our visibility, the same transfer point as everywhere else in SikkerKey.