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).