Client-side encryption-at-rest (AES-GCM-256)

Encrypt files in the browser before they ever leave the device

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

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

Configuration
AjaxUploader.create('#uploader', {
 uploadUrl: '/ajaxupload.axd/upload',
 encrypt: true, // or 'aes-gcm-256' encryptPassword: 'shhh',
 // OR for a prompt-driven UI: // encryptPassword: () => promptUser(), // OR for a pre-derived key: // encryptKey: cryptoKey, encryptIterations: 200000 // PBKDF2 iterations (default)
});
Wire-protocol headers (sent on every chunk / single upload)
X-Mu-Encryption-Algo: aes-gcm-256
X-Mu-Encryption-Salt: <16 random bytes, base64url>
X-Mu-Encryption-IV: <12 random bytes, base64url>
X-Mu-Encryption-Iter: 200000
X-Mu-Encryption-Original-Name: <URL-encoded cleartext filename>
X-Mu-Encryption-Original-Size: <cleartext byte count>
Decryption (browser-side download viewer)
// Static helper: pass the ciphertext blob, password, salt, iv
const cleartext = await AjaxUploader.decryptFile(
 ciphertextBlob,
 password,
 saltFromHeader,
 ivFromHeader,
 200000 // iterations, must match upload
);
// cleartext is a Blob - show it, save it, or pipe it to a viewer
Decryption (server-side, e.g. .NET 8+)
// PBKDF2 + AES-GCM are first-class in .NET 8's System.Security.Cryptography.
using var pbkdf2 = new Rfc2898DeriveBytes(password, salt, 200000, HashAlgorithmName.SHA256);
var key = pbkdf2.GetBytes(32);
using var aes = new AesGcm(key, 16);
var ciphertext = encryptedFile.AsSpan(0, encryptedFile.Length - 16);
var tag = encryptedFile.AsSpan(encryptedFile.Length - 16);
var plaintext = new byte[ciphertext.Length];
aes.Decrypt(iv, ciphertext, tag, plaintext);
Algorithm details
  • Cipher: AES-256-GCM (authenticated encryption - tamper-evident).
  • Key derivation: PBKDF2-HMAC-SHA256, 200 000 iterations by default (tunable via encryptIterations).
  • Salt: 128-bit random per file. Stored alongside ciphertext; never reused.
  • IV (nonce): 96-bit random per file. Stored alongside ciphertext; never reused with the same key.
  • Auth tag: 128-bit GCM tag appended to ciphertext (handled automatically by SubtleCrypto).
Failure modes & safety
  • Insecure context (http://): SubtleCrypto is unavailable and the encrypt task fails fast with encryption_failed: SubtleCrypto unavailable. No silent fallback to cleartext - that's the right default for compliance use.
  • Missing password: queued files fail with a clear error before any byte leaves the browser.
  • Resume after reload: when paired with persistState, encryption is preserved (the ciphertext file is what's persisted, not the cleartext).