SECURITY ARCHITECTURE

Built to be unreadable.
By design.

Zero-knowledge isn't a policy — it's a technical constraint. Here's exactly how Wispr makes it impossible for us to read your data.

AES-256-GCM

Encryption algorithm

PBKDF2 / AES-KW

Password key wrapping

100,000

PBKDF2 iterations (SHA-256)

Encryption

Everything encrypted in your browser — nothing readable ever leaves your device.

Client-Side AES-256-GCM

Your browser generates a random 256-bit AES key and encrypts your message or file using GCM mode before any network request is made.

  • Key generated with Web Crypto API (CSPRNG)
  • GCM mode provides authenticated encryption — tampering is detectable
  • 12-byte random IV generated per secret
  • Server receives only: ciphertext + IV (both useless without the key)

Key Never Sent to Server

The encryption key lives only in the URL fragment (#key=...). Browsers never include the fragment in HTTP requests — it's a web standard constraint, not a policy.

  • URL fragment (#hash) is client-only — not in request headers, logs, or referers
  • If you share the link via email, the key is still never exposed to servers
  • Even if our server is fully compromised, keys cannot be extracted from logs

Password Key Wrapping (PBKDF2 + AES-KW)

Password-protected secrets use PBKDF2 to derive a wrapping key from your password locally, then wrap the AES key with AES-KW. The password never leaves your browser.

  • PBKDF2-SHA256 with 100,000 iterations — computationally expensive to brute-force
  • Random 16-byte salt generated per secret
  • Wrapped key stored on server — useless without the original password
  • For password-protected secrets, the URL contains no key fragment

Authenticated Encryption

AES-GCM includes a built-in authentication tag. Any tampering with the ciphertext — by us, an attacker, or a network middleman — will cause decryption to fail.

  • 128-bit authentication tag per encrypted block
  • Decryption fails immediately if ciphertext is modified in transit
  • Prevents chosen-ciphertext attacks
  • No silent failures — corruption is always detected

Anonymity & Privacy

What we collect, what we don't, and how we minimize your exposure.

No Tracking, No Analytics

Wispr contains zero third-party scripts. No Google Analytics, no Mixpanel, no Sentry, no ad networks. Your visits generate no fingerprinting surface.

  • No JavaScript from external domains loads in the browser
  • No localStorage/sessionStorage for tracking — only UI state
  • No cookies of any kind
  • Content Security Policy blocks any unauthorized external scripts

Minimal Server Logs

Server logs record only: HTTP method, URL path (no query strings), status code, and response time. IP addresses appear in system-level logs and are purged within 30 days.

  • Query strings stripped from all log entries
  • No message content, keys, or passwords ever appear in logs
  • URL fragments are never sent to the server — not loggable by definition
  • Log purge cycle: ≤30 days

Tor & VPN Friendly

Wispr works over Tor Browser and VPNs without degradation. We don't block Tor exit nodes or flag unusual IP ranges. Anonymity tools should work with anonymity tools.

  • No Tor exit node blocking
  • No CAPTCHA challenges triggered by unusual IPs
  • No behavioral fingerprinting or IP-reputation gating
  • JavaScript-minimal design works with strict Tor Browser settings

No Account Required

Creating or receiving a secret requires zero personal information — no email, no phone, no name. The only identifier is the random secret ID.

  • Secret IDs are 16-character nanoid strings (~80 bits of entropy)
  • No user accounts, sessions, or authentication tokens
  • No correlation between secrets you create
  • Custom slugs are optional — auto-generated IDs are fully random

Infrastructure Security

Server-side hardening and transport security.

Security Headers

Every API response includes a strict set of security headers to prevent common web attacks.

  • Content-Security-Policy — blocks unauthorized script execution
  • Strict-Transport-Security (HSTS, 1 year, preload) — forces HTTPS
  • X-Content-Type-Options: nosniff — prevents MIME sniffing
  • Referrer-Policy: no-referrer — prevents URL leakage in referers
  • Permissions-Policy — disables camera, mic, geolocation, payment
  • X-Frame-Options (via CSP frame-ancestors: none) — prevents clickjacking

Rate Limiting

API endpoints are rate-limited to prevent brute-force attacks and abuse.

  • Global: 200 requests per 15 minutes per IP
  • Secret creation: 30 secrets per hour per IP
  • Reveal endpoint: 60 attempts per 15 minutes per IP
  • Standard rate-limit headers returned on all responses

Burn-After-Reading — Server Enforced

Burn-after-reading is enforced at the database level. On reveal, the row is hard-deleted from PostgreSQL immediately — not flagged, not soft-deleted.

  • Hard DELETE on reveal — permanent and unrecoverable
  • Even if someone captures the link, revealing once burns it for everyone
  • Expired secrets deleted on next access attempt
  • Max-view limits enforced atomically with deletion

Zero Server-Side Key Material

The server's database contains only ciphertext, IVs, and (for password secrets) wrapped key material. The actual encryption key is architecturally absent.

  • No plaintext content ever written to the database
  • No raw AES keys ever transmitted to or stored by the server
  • Wrapped keys are useless without the user's password
  • A full database dump would contain only encrypted blobs

Input Validation & Type Safety

All API inputs are validated with Zod schemas generated from the OpenAPI specification. Malformed requests are rejected at the boundary.

  • Zod schema validation on every request body
  • Type-safe Express routes — no untyped req.body access
  • 50MB JSON body limit enforced at the application layer
  • Custom slugs validated against strict alphanumeric regex

Database Encryption at Rest

PostgreSQL data is encrypted at rest on the server infrastructure. Even physical access to storage would not expose readable data.

  • Database encrypted at rest by hosting provider
  • Combined with application-level AES-256-GCM encryption per secret
  • Two layers: transport TLS + storage encryption
  • No database credentials in application code — environment variable only

Threat Model

What Wispr protects against — and what it doesn't claim to.

Wispr server compromise / database breach

Server stores only encrypted blobs. Without the key, a full database dump is useless.

Network interception (MITM)

TLS encrypts transport. The key is in the URL fragment, which is never transmitted.

Server-side logging of content

No plaintext content, keys, or passwords are ever present on the server to log.

~

Recipient's device being compromised

Once decrypted in the recipient's browser, Wispr has no control over what happens to it.

~

Screenshots or copy-paste by the recipient

Burn-after-reading prevents re-access, but cannot prevent the recipient from saving the plaintext.

~

Weak or reused passwords

PBKDF2 with 100k iterations slows brute-force, but a guessable password is still guessable.

Legal compulsion to produce data

We can only produce what we store: encrypted blobs and metadata. No keys, no plaintext. We cannot decrypt on behalf of any party.

~

Link URL shared over an insecure channel

If the link (including the #key fragment) is intercepted before the intended recipient clicks it, the secret can be read. Treat the link itself as the secret.

Legal & Government Requests

What happens when law enforcement asks for your data.

What We Can Produce Under Compulsion

If compelled by law, we can hand over only what we store — which is deliberately minimal and useless without keys we don't have.

  • Encrypted ciphertext (AES-256-GCM — unusable without key)
  • IV and wrapped key material (for password-protected secrets)
  • Metadata: creation time, expiry, burn setting, view count
  • No content, no keys, no passwords — not by policy, by architecture

Warrant Canary

We publish a warrant canary that is updated regularly. If it goes dark, it may indicate we have received a secret government order we cannot disclose.

  • Canary updated at regular intervals
  • Absence of update signals possible legal constraint
  • See the /canary page for the current status
  • Read about warrant canaries in our blog

Minimal Metadata Retention

The only metadata we retain is what's functionally necessary to serve the secret. We do not log who created a secret or who revealed it.

  • Creation timestamp (needed for expiry logic)
  • View count (needed for max-views enforcement)
  • No sender identity, no recipient identity
  • No IP-to-secret correlation in application-level logs

Proton Mail Contact

Our contact email is hosted on Proton Mail — end-to-end encrypted by default when writing from a Proton address. We picked it deliberately.

  • [email protected] — Proton-to-Proton messages are E2E encrypted
  • Security disclosures should use this address
  • Or use a Wispr link itself for maximum privacy

Technical Specifications

Encryption
AES-256-GCM

Authenticated encryption with 128-bit tag

Key Derivation
PBKDF2-SHA256

100,000 iterations, random 16-byte salt

Key Wrapping
AES-KW (256-bit)

RFC 3394 — wraps the AES key for password secrets

IV Size
12 bytes (96-bit)

Randomly generated per secret via CSPRNG

Key Size
256 bits

Generated via Web Crypto API

Secret ID Entropy
~80 bits

16-character nanoid, URL-safe alphabet

Crypto Runtime
Web Crypto API

Browser-native, no third-party crypto libraries

Transport
TLS 1.2+ (HTTPS)

HSTS enforced, 1-year max-age with preload

Database
PostgreSQL

Encrypted at rest on server infrastructure

Log Retention
≤ 30 days

System-level logs only; no content

Body Limit
50 MB

Maximum encrypted payload per request

Built
2026

Open-source, auditable

Found a vulnerability?

We take security reports seriously. If you've found a vulnerability, please disclose responsibly — we aim to respond within 24 hours. We won't pursue legal action against good-faith researchers.

[email protected]

Use Wispr itself to send an encrypted report