DEV Community

FileShot
FileShot

Posted on

How Zero-Knowledge File Sharing Works: AES-256-GCM in the Browser

When you upload a file to most cloud services — Google Drive, Dropbox, WeTransfer — the server receives your file in plaintext before encrypting it on their end. That means they can read it. They have the key.

Zero-knowledge file sharing is different: the server never receives the plaintext file at all. Encryption happens entirely in the browser before a single byte is transmitted.

Here's exactly how it works, using FileShot.io as a working example (MIT open source, self-hostable).

The Core Concept: URL Fragments

The trick that makes zero-knowledge sharing practical is the URL fragment — the part after the #.

https://fileshot.io/d/abc123#AES_KEY_HERE
Enter fullscreen mode Exit fullscreen mode

Browsers never send the fragment to the server. It is not included in HTTP requests, not logged in server access logs, not sent in Referer headers. The #KEY part stays purely client-side.

This is how the decryption key travels with the share link without the server ever seeing it.

The Encryption Flow

  1. Key generation: crypto.subtle.generateKey({ name: "AES-GCM", length: 256 }, true, ["encrypt", "decrypt"])
  2. Encrypt before upload: File bytes are encrypted with AES-256-GCM locally; the server receives only ciphertext
  3. Key export and encoding: The key is exported and base64url-encoded into the URL fragment
  4. Share: The recipient gets a link like fileshot.io/d/ID#KEY
  5. Decrypt: Recipient's browser extracts the key from location.hash, fetches the ciphertext, decrypts locally

Why AES-GCM Specifically

AES-GCM is an Authenticated Encryption with Associated Data (AEAD) mode. It provides:

  • Confidentiality: Ciphertext reveals nothing about plaintext
  • Integrity: Any tampering with the ciphertext is detectable — the subtle.decrypt call will throw an error
  • Performance: Hardware-accelerated on modern CPUs (AES-NI)

GCM uses a random 96-bit nonce (IV) per encryption operation, which is stored alongside the ciphertext.

What the Server Sees

The server receives:

  • An opaque blob of ciphertext
  • A random file ID
  • An expiry time

The server cannot decrypt the file even if compelled — it has never seen the key.

Try it

FileShot.io is a production implementation of this pattern, free to use, MIT licensed, and self-hostable at github.com/FileShot/FileShotZKE.

Zero-knowledge encryption is not complicated — it just requires keeping the key out of the server's reach entirely.

Top comments (0)