Skip to main content

NullPaste

Security

This page documents exactly how NullPaste protects paste content: the cryptography behind end-to-end encrypted pastes, what the server can and cannot see, how passwords and delete codes are stored, and how to report a vulnerability. For what data is retained and why, see the privacy policy.

Encryption (E2E pastes)

When you enable end-to-end encryption, your browser encrypts the paste with AES-256-GCM via the Web Crypto API before anything is uploaded. A random 256-bit key is generated with crypto.subtle.generateKey, and every encryption uses a fresh random 12-byte IV from crypto.getRandomValues. GCM appends a 16-byte authentication tag, so any tampering with the ciphertext makes decryption fail rather than produce altered content.

The server stores only the ciphertext, in a versioned format: enc1:<base64url iv>:<base64url ciphertext>. The key and IV are base64url-encoded, and the key travels only in the URL fragment of your share link: #key=<base64url 32-byte key>. Browsers never send the fragment to any server, and the key is never stored server-side. Decryption happens only in the browser of whoever opens the link.

The flip side of this design: if the fragment is lost, the paste is permanently unreadable — by anyone, including NullPaste. There is no recovery mechanism.

What the server can and cannot see

Unencrypted pasteserver sees: full plaintext

The server stores the content as plaintext. The URL is unlisted — paste pages carry a noindex directive and ids are unguessable 12-character strings — but the link is not secret: anyone who obtains it can read the paste.

Password-protected pasteserver sees: full plaintext

The password is access control, not encryption. The server verifies an Argon2id hash before serving the paste, but it still stores the content as plaintext. This is not end-to-end encryption.

End-to-end encrypted pasteserver sees: ciphertext only

Your browser encrypts the content before upload. The server stores only the enc1: ciphertext and never receives the key, so it cannot read the paste.

Passwords

Paste passwords are hashed with Argon2id (memory cost 19456 KiB, time cost 2, parallelism 1) and only the hash is stored. Verification happens server-side: the server checks the hash before it serves the paste content.

A password is not the same as end-to-end encryption. It controls who can open the paste, but the server still stores the content as plaintext. For secrets that must be unreadable to the server itself, enable end-to-end encryption.

Delete codes

Every paste receives a one-time delete code: an 18-character nanoid with roughly 107 bits of entropy, shown exactly once on the creation success screen. The server keeps only its SHA-256 hash and compares submissions with a timing-safe comparison. Deletion requires no account — just the code.

Burn-after-read

Opening a burn link first shows an interstitial — "Opening this deletes the paste permanently." — and the content is only fetched and consumed after an explicit human confirmation. Link-preview bots (Slack, Discord, Twitter, WhatsApp, Telegram, iMessage and similar) never consume the paste, because consumption happens only via the confirmed fetch and paste responses are served with Cache-Control: no-store.

Consumption is atomic: if two confirmed requests race, only one can read the paste. Afterwards the row content is wiped and a "burned at" tombstone is kept for about 30 days, so the link shows a dedicated burned state instead of a generic 404.

Honest caveats: anyone holding the link before it is consumed can be the one who burns it, and whoever reads the paste can copy or forward its content. Burn-after-read guarantees the paste is served once — it cannot guarantee who that one reader is.

HTTP security headers

Every response is served with the following headers:

  • Content-Security-Policy no unsafe-eval; scripts load only from this origin (plus challenges.cloudflare.com when the optional Turnstile CAPTCHA is enabled)
  • Strict-Transport-Security enforced in production so browsers only connect over HTTPS
  • X-Frame-Options + frame-ancestors 'none' pages can never be embedded in frames on other sites (the CSP frame-ancestors directive governs in modern browsers, whatever the CDN sets X-Frame-Options to)
  • Referrer-Policy: no-referrer sent by the application site-wide; a CDN in front may relax it to strict-origin-when-cross-origin, which still means paste IDs never leak to other sites through the Referer header
  • X-Content-Type-Options: nosniff responses are never content-type sniffed
  • Cross-Origin-Opener-Policy: same-origin isolates the browsing context from cross-origin windows
  • Permissions-Policy restrictive — powerful browser features are disabled
  • Cache-Control: no-store responses are never cached by browsers or intermediaries

Rate limiting

Rate limiting uses identifiers derived from the client IP with a one-way HMAC-SHA256. The raw IP address is never stored or logged, and the original address cannot be recovered from the identifier.

Reporting a vulnerability

If you find a security issue in NullPaste, please report it privately to [email protected] rather than disclosing it publicly. Allow up to 90 days for a fix before public disclosure. There is no bounty program, but reports are genuinely appreciated and acted on.

This policy is also published in machine-readable form at /.well-known/security.txt.