Direct-to-S3 / Azure tus 1.0 resume IndexedDB Golden Retriever 30 locales Webcam & screen capture

Zero-knowledge upload via AES-GCM-256 + PBKDF2

Every queued file is encrypted in the browser with AES-GCM-256 before upload begins; your ASP.NET Core server only ever sees ciphertext. Decryption requires the password and the per-file salt + IV emitted as X-Mu-Encryption-* headers. Compliance-friendly default for HIPAA / GDPR confidentiality workflows. None of Uppy, FilePond, Dropzone, or FineUploader offer this built-in.

What you should see: each file is encrypted in the browser with AES-GCM before any bytes leave the page, so the server only ever receives ciphertext. Auto-upload is off so you can see the encryption step happen first.

The password never leaves the browser. The server only sees ciphertext + the salt & IV needed to decrypt on retrieval.

Drag & drop files here, or paste from clipboard
Try it: drop a file, hit upload, inspect the request body in DevTools -> Network. The payload is unintelligible binary, plus six request headers record the algorithm + salt + IV + iterations + original filename + original size.

Configuration

CoreUpload.create('#uploader', {
 uploadUrl: '/api/upload/upload',
 encrypt: true, // or 'aes-gcm-256' encryptPassword: 'shhh',
 encryptIterations: 200000 // PBKDF2 default
});

Server-side decryption (.NET 8)

using System.Security.Cryptography;

// Read salt + IV + iter from request headers
var salt = Base64UrlDecode(req.Headers["X-Mu-Encryption-Salt"]);
var iv = Base64UrlDecode(req.Headers["X-Mu-Encryption-IV"]);
var iter = int.Parse(req.Headers["X-Mu-Encryption-Iter"]);

using var pbkdf2 = new Rfc2898DeriveBytes(password, salt, iter, HashAlgorithmName.SHA256);
var key = pbkdf2.GetBytes(32);
using var aes = new AesGcm(key, 16);
var ciphertext = encryptedBytes.AsSpan(0, encryptedBytes.Length - 16);
var tag = encryptedBytes.AsSpan(encryptedBytes.Length - 16);
var plaintext = new byte[ciphertext.Length];
aes.Decrypt(iv, ciphertext, tag, plaintext);

Algorithm details

  • Cipher: AES-256-GCM (authenticated encryption - tamper-evident).
  • KDF: PBKDF2-HMAC-SHA256, 200,000 iterations by default.
  • Salt: 128-bit random per file. Never reused.
  • IV (nonce): 96-bit random per file. Never reused with the same key.
  • Auth tag: 128-bit GCM tag appended to ciphertext.

Safety

  • Insecure context: on http://, the encrypt task fails fast - no silent fallback to cleartext.
  • Missing password: queued files fail with a clear error before any byte leaves the browser.
  • Resume: when paired with persistState, encryption is preserved - the ciphertext file is what gets persisted.