Security
Encrypted pastes are encrypted in your browser before anything is sent. The server stores ciphertext and never receives your password.
This page describes exactly how that works, and — just as importantly — what it does not protect you from.
Parameters
| Cipher | AES-256-GCM (authenticated encryption) |
|---|---|
| Key derivation | PBKDF2 with SHA-256 |
| Iterations | 310,000 (OWASP 2023 recommendation) |
| Key length | 256 bits |
| Salt | 16 bytes, randomly generated per paste |
| Initialisation vector | 12 bytes, randomly generated per paste |
| Stored format | base64(salt ‖ iv ‖ ciphertext) |
| Randomness | crypto.getRandomValues (Web Crypto API) |
What happens when you encrypt
You enter a password. The browser generates a random 16-byte salt and runs PBKDF2-SHA256 over your password for 310,000 iterations to derive a 256-bit key. The iteration count is deliberate cost: it makes each guess in a brute-force attempt expensive.
A random 12-byte initialisation vector is generated, and the content is encrypted with AES-256-GCM. GCM is an authenticated mode, so decryption fails loudly if the ciphertext has been altered rather than returning corrupted output.
The salt, IV and ciphertext are concatenated, base64-encoded, and sent to the server. The password is not sent. The derived key is not sent. Neither is stored anywhere.
How the password travels in a link
A self-decrypting link puts the password after the # in the URL. Browsers do not transmit the fragment to the server — it never appears in a request, in server logs, or in access records.
The page reads it, decrypts locally, and clears it from browser history. It does still travel through whatever channel you send the link over, so a link containing the password is only as private as the chat or email carrying it.
What the server stores
Every paste is one database row with four fields:
- an id and an eight-character public id used in the URL
- the content — Brotli-compressed for normal pastes, ciphertext for encrypted ones
- a flag marking whether it is encrypted
- a created-at timestamp
There is no IP address, no user agent, no account, no email and no author field. Not as a policy that could change — those columns do not exist.
What this does not protect against
A weak password. 310,000 PBKDF2 iterations raise the cost per guess; they do not save a password that appears in a wordlist. The strength of an encrypted paste is the strength of the password you chose.
Losing the password. There is no recovery, no reset and no back door. If it is lost, the content is unrecoverable — that is the design working, not failing.
Unencrypted pastes. A normal paste is compressed, not encrypted. Anyone with the link can read it, and so can the server. The URL is eight characters from a 36-character alphabet — about 2.8 trillion combinations, which is impractical to guess but is obscurity, not access control.
A compromised browser or page.Encryption happens in your browser, so it inherits your browser's security. Malware or a malicious extension that can read the page can read the content before it is encrypted.
Remembered passwords.Choosing to remember a password stores it in that browser's localStorage in readable form. It is convenient and it is a tradeoff; anyone with access to that browser profile can read it. There is a “forget” control for this reason.
Pastes are not deleted automatically. Expiry is on the roadmap but not built. Today a paste persists until removed.
Verifying this yourself
Do not take a security page at its word. The encryption is about a hundred lines in src/lib/crypto.ts, and the whole project is open source — read it, or watch the network tab while you create an encrypted paste and confirm the request body contains nothing but ciphertext.
Create an encrypted paste
Pick a strong password, share the link, and the content stays readable only to whoever has that password.