Skip to main content

Command Palette

Search for a command to run...

The Developer's Guide to Private File Sharing: Why "Encrypted" Doesn't Always Mean Private

Updated
3 min read
F
Building FileShot.io — a privacy-first, zero-knowledge file sharing platform. All encryption happens in your browser with AES-256-GCM. The server is 100% blind to your files. Web app, desktop (Electron), Chrome extension, and Android APK. GPL v2 open source.

When you upload a file to most services and they say "your data is encrypted," ask yourself: who holds the key?

In almost every case, the answer is the service itself. That means encryption is protecting you from third parties, but not from the platform you just uploaded to. If their database is breached, subpoenaed, or their team goes rogue, your files are accessible.

This distinction matters more than most developers realize.

Server-Side Encryption vs. Zero-Knowledge Encryption

Server-side encryption (what most services do):

  • Your file is encrypted at rest using a key the server generates and stores

    • The server can decrypt your file at any time

      • TLS protects data in transit, but once the server receives it, all bets are off
    • Zero-knowledge encryption (what privacy-first services do):

    • Encryption happens in the browser before any data is sent to the server

      • The decryption key never reaches the server

        • Even the developer cannot access your files
      • How Zero-Knowledge File Sharing Works

    • The trick is actually elegant. Using the [Web Crypto API](https://developer.mozilla.org/en-US/docs/Web/API/Web\_Crypto\_API), you can do all of this in the browser:

    1. Generate an AES-256-GCM key entirely client-side

      1. Encrypt the file locally before upload

        1. Upload only the ciphertext

          1. Embed the key in the URL fragment (the # part of the URL)
        2. URL fragments are never sent to the server — they are a browser-only construct. So a share link like https://example.com/file/abc123#decryptionKey means the server only ever sees /file/abc123.

      2. The recipient opens the link, the browser reads the key from window.location.hash, decrypts the ciphertext locally, and the server was never involved in the key lifecycle.

    2. A Practical Example: FileShot

  • FileShot implements exactly this model. Every file is encrypted with AES-256-GCM in the browser using the Web Crypto API before a single byte is sent to the server. The share URL contains the decryption key in the fragment.

Key properties of this design:

  • No key escrow — FileShot's servers hold zero keys

    • No plaintext ever in transit — what the server stores is computationally useless without the key

      • No backend changes needed — the security property is enforced by the client

        • Files expire automatically — metadata deletion is clean because there's nothing sensitive server-side
      • The Tradeoffs

    • Zero-knowledge isn't free. You give up:

    • Password recovery — if you lose the URL with the key fragment, the file is gone

      • Server-side search — you can't search encrypted content

        • Deduplication — identical files encrypt differently due to unique IVs
      • For file sharing use cases, these tradeoffs are usually worth it.

    • The Takeaway

  • The next time a service claims to be "encrypted," probe deeper. Ask: is the key ever on the server? If yes, you're trusting the platform. If no, you have real zero-knowledge privacy.

For developers building tools where user privacy matters, the Web Crypto API makes implementing true zero-knowledge architecture surprisingly straightforward.


If you're curious about the implementation details, I wrote a technical deep-dive on [Dev.to](https://dev.to/graysoftdev/how-i-built-zero-knowledge-file-sharing-using-the-web-crypto-api-no-server-ever-sees-your-data-28cp) covering the actual code.

Try it live at [FileShot.io](https://fileshot.io).

More from this blog

FileShot

7 posts