Skip to main content
encryptd gives you two core security guarantees: confidentiality (your .env values cannot be read without the passphrase) and integrity (any modification to the encrypted file is detected before decryption produces any output). Both properties come from the algorithm choices made in the Rust native addon. Understanding these choices helps you reason about what is and isn’t protected.

Algorithm Choices

AES-256-GCM

AES-256-GCM (Advanced Encryption Standard, 256-bit key, Galois/Counter Mode) is an authenticated encryption with associated data (AEAD) cipher. It is the same algorithm used in TLS 1.3, SSH, and cloud storage encryption. Using it gives you:
  • Confidentiality: the plaintext is indistinguishable from random bytes without the key.
  • Integrity: the 16-byte authentication tag mathematically binds the key, nonce, and ciphertext together. Altering any byte of the ciphertext causes tag verification to fail.
  • Performance: modern CPUs include AES hardware acceleration (AES-NI), making encryption and decryption fast even for large files.

PBKDF2-HMAC-SHA256

Passphrases entered by humans are rarely uniform random values. PBKDF2 (Password-Based Key Derivation Function 2) with HMAC-SHA256 transforms your passphrase into a uniform 256-bit key suitable for AES-256-GCM. encryptd runs PBKDF2 with 100,000 iterations, which has two effects:
  • Key strengthening: a short or common passphrase becomes a full-entropy 256-bit key.
  • Brute-force resistance: an attacker trying to guess your passphrase must run 100,000 SHA-256 computations per guess. At typical hardware speeds, exhausting even a modest passphrase space takes significant time.

Random 16-Byte Salt

The salt is generated fresh for every encryption using OsRng, the operating system’s cryptographically secure random number generator. It serves two purposes:
  • Prevents rainbow table attacks: precomputed passphrase-to-key tables are useless because each encrypted file uses a unique salt.
  • Ensures key uniqueness: even if the same passphrase encrypts two different files, the derived AES keys will differ.

Random 12-Byte Nonce

The nonce (also called IV in the payload) is also generated fresh per encryption via Aes256Gcm::generate_nonce. Within AES-256-GCM, reusing a nonce with the same key would catastrophically weaken the cipher. Because encryptd generates a new nonce (and a new key, via a new salt) on every run, this risk is eliminated.

16-Byte GCM Authentication Tag

At the end of every AES-256-GCM encryption, the cipher appends a 128-bit (16-byte) authentication tag derived from the key, nonce, and ciphertext. Decryption verifies this tag before returning any output. The tag provides:
  • Tamper detection: a single flipped bit in content, tag, salt, or iv causes decryption to fail outright.
  • Wrong-passphrase detection: a different passphrase derives a different key, which produces a different expected tag, so decryption fails cleanly.

Tamper Detection

If anyone modifies your .env.enc file by editing the JSON, truncating it, or flipping bytes decryption will fail with an error similar to:
No partial plaintext is ever returned. AES-256-GCM’s tag verification is an all-or-nothing operation performed inside the Rust native addon before any bytes reach JavaScript.

What encryptd Does NOT Protect Against

The passphrase is the single point of protection. If an attacker obtains both your passphrase and your .env.enc file, they can decrypt its contents. Protect your passphrase with the same care you would give to the plaintext secrets themselves.
  • A compromised passphrase: PBKDF2 and AES-256-GCM are only as strong as the secret you provide. A weak or leaked passphrase negates all cryptographic protection.
  • Runtime memory inspection: once config() loads decrypted values into process.env, those values exist in plaintext in Node.js process memory. Any code or dependency running in the same process can read process.env.
  • Encrypted file exfiltration paired with passphrase theft: encryptd protects the file at rest. It cannot prevent an attacker who already has both artifacts from decrypting offline.

Passphrase Best Practices

A strong passphrase is critical to the security of everything encryptd protects.
  • Use at least 32 random characters. A password manager or openssl rand -base64 32 gives you a suitable value.
  • Never hardcode the passphrase in source code. Commit the .env.enc file, not the passphrase.
  • Store it in a secrets manager. Good options include GitHub Actions encrypted secrets, AWS Secrets Manager, HashiCorp Vault, and similar tools. Inject it as the ENV_PASSPHRASE environment variable at runtime.
  • Rotate it if compromised. Re-encrypt your .env with a new passphrase and update every location where the old passphrase was stored.
  • Limit access. Only the people and systems that need to decrypt the file should ever see the passphrase.