
As a student developer, I wanted to deeply understand cryptography and backend security. I noticed a glaring issue with many popular "secure" file-sharing and messaging platforms: they handle your decryption keys on their backend.
If their servers get breached, or if an administrator decides to look, your data is compromised.
I wanted to build an architecture where the server mathematically cannot read the payloads passing through it. To solve this, I built ZeroKey, an open-source, end-to-end encrypted, burn-after-reading payload delivery system.
Here is a breakdown of how I built it using Vanilla JavaScript, Vercel, and Supabase.
The Tech Stack
- Frontend: Vanilla JS, HTML5, Tailwind CSS
-
Cryptography: Native Web Crypto API (
window.crypto.subtle) - Backend: Vercel Serverless Functions (Node.js)
- Database: Supabase (PostgreSQL & Storage)
1. Client-Side Encryption (AES-256-GCM)
The golden rule of ZeroKey is that plaintext never leaves the browser.
When a user enters a secret or attaches a file (up to 2MB), the frontend uses window.crypto.getRandomValues to generate a secure 16-byte salt and a 12-byte Initialization Vector (IV).
If the user provides a custom PIN, I use PBKDF2 with 100,000 SHA-256 iterations to derive a robust 256-bit cryptographic key. The payload is then encrypted using AES-256-GCM.
2. The URL Fragment Exploit (Zero-Knowledge Routing)
How do you share the decryption key with the recipient without sending it to the database? You use the URL fragment.
When ZeroKey generates a shareable link, it looks like this:
https://zerokey.vercel.app/view?id=[UUID]&iv=[Base64]&salt=[Base64]#[Decryption_Key]
By design, web browsers do not send the URL fragment (anything after the # symbol) to the server. When the recipient clicks the link, Vercel and Supabase only see the id, iv, and salt. The actual decryption key remains entirely on the client side, allowing the recipient's browser to execute the AES-GCM decryption locally.
The server is completely blind.
3. True Burn-After-Reading (Supabase RLS)
To ensure zero data retention, the Supabase database is locked down strictly using Row-Level Security (RLS). The public internet has zero access to read or write data.
Instead, the frontend communicates with Vercel Serverless Functions. Once the recipient's browser successfully decrypts the payload, it sends a destruction signal to a Vercel API route. This route uses a secure Service Role key to execute a hard DELETE command on both the PostgreSQL row and the encrypted Supabase Storage blob.
If a third party intercepts the link after it has been opened, there is nothing left to fetch.
4. Extra Security Layers
To make the vault even more secure, I implemented:
- Geofencing: Senders can lock the decryption to a 50-meter radius of their current GPS coordinates using the Geolocation API.
- Anti-Bot Gates: Social media apps (like WhatsApp or iMessage) often send bots to "preview" links, which would accidentally trigger the burn protocol. I implemented a biometric/human-verification gate before the database is ever queried.
The Code
Building this taught me more about security pipelines than any textbook. The entire project is open-source, and I would genuinely appreciate code reviews, architectural critiques, or pull requests from the community.
- Live Demo: zerokey.vercel.app
- GitHub Repository: github.com/kdippan/zerokey
Let me know what you think of the architecture in the comments. Have you ever worked with the Web Crypto API?
Top comments (0)