Skip to content

Security

Security architecture

What VaultDrop guarantees, how, and — just as important — what it does not. No certifications are claimed here; this page describes the system as built.

Summary

  • End-to-end encryption. Secrets are encrypted in the sender's browser with AES-256-GCM and decrypted in the recipient's browser. The key travels only in the link's URL fragment, which browsers do not send to servers.
  • Zero-knowledge storage. The server stores ciphertext, a random IV, KDF parameters and a one-way verifier. It never receives plaintext, link keys or passphrases, and cannot decrypt what it stores.
  • Exactly-once redemption. Opening a one-time secret locks its row, checks state and expiry, marks it consumed and deletes the ciphertext in a single database transaction. Concurrent requests cannot both succeed.
  • Wrong passphrases don't consume secrets. The server can check a passphrase-derived proof without learning the passphrase. After 10 failed attempts (configurable 3–20) the secret is destroyed to stop online guessing.

Cryptographic design

StepConstruction
Link key32 bytes from the browser's CSPRNG (crypto.getRandomValues), unique per secret
Passphrase stretchingArgon2id, 19 MiB memory, 2 iterations, 1 lane, 16-byte random salt (parameters stored per secret, bounded server-side)
Key derivationHKDF-SHA256 over link key ‖ Argon2id output, with distinct info labels for the encryption key and the redemption proof
EncryptionAES-256-GCM, 96-bit random IV, 128-bit tag, fixed associated data; plaintext padded to 256-byte buckets
Redemption proofServer stores SHA-256(proof); the recipient sends the proof; comparison is constant-time

All primitives come from the browser's Web Crypto API, except Argon2id, which uses the audited @noble/hashes library. No custom ciphers are used. The full design and its analysis are in docs/encryption.md in the repository.

What the server can see

DataVisible to VaultDrop?
Secret contentNo — ciphertext only
Link key, passphraseNo
Approximate sizeYes, rounded up to 256 bytes
Creation, expiry, open and destroy times; view countYes
Whether a passphrase is requiredYes
Private labels (signed-in users)Yes — labels are not encrypted; never put secrets in them
IP addressesUsed transiently for rate limiting as keyed pseudonyms; not stored raw by VaultDrop. Session records keep the sign-in IP for your security page.

Threat model

Database or backup disclosure

An attacker with a full database copy gets ciphertext without keys. Passphrase-protected secrets additionally require the link key before any guessing is possible.

Network interception

Production deployments must use HTTPS with HSTS. Even a TLS-terminating proxy sees only ciphertext, because keys stay in the URL fragment.

Enumeration and brute force

Identifiers are 128-bit random. Redemption requires a proof derived from the 256-bit link key. Requests are rate-limited per client and per secret, and failed proofs are capped per secret.

Race conditions and replay

Redemption uses a row lock plus a conditional update on the observed state and view count. A replayed proof after consumption receives "already opened". Tests run 25 concurrent redemptions and assert exactly one success.

Cross-site scripting and CSRF

A per-request nonce-based Content Security Policy allows only our own scripts; no third-party scripts, fonts or analytics are loaded. Cookie-authenticated state changes require a same-origin request. Session cookies are HTTP-only and SameSite=Lax.

Accidental logging

Structured logs redact sensitive fields, strip URL fragments and replace secret identifiers in paths. Ciphertext, proofs, keys and tokens are never logged by design.

Insider access

Operators with database access still cannot decrypt secrets. The admin area has no capability to read secret contents or recover destroyed ones.

Deletion and its limits

The encrypted payload row is deleted in the same transaction that consumes, expires or destroys a secret. PostgreSQL may retain deleted data in free pages, the write-ahead log, replicas and backups until those are recycled; backups are kept according to the operator's retention policy. Because that data is ciphertext without keys, residual copies are not readable — but we do not claim instantaneous physical erasure everywhere.

Known limitations

  • Anyone who obtains the full link (and passphrase, if any) before the intended recipient can open the secret. The receipt shows when it was opened.
  • Browser-based cryptography depends on the integrity of the code served to you. A compromised server could serve malicious JavaScript; the CLI avoids this risk.
  • Screenshots, copying and malware on the recipient's device are outside VaultDrop's control.
  • Ciphertext size reveals the secret's length to within 256 bytes.

Reporting a vulnerability

Please report security issues privately — see the contact page or /.well-known/security.txt. We ask that you give us reasonable time to fix issues before public disclosure.