Lab: check that the vault file is opaque
Take the claim that the local vault is encrypted at rest and verify it yourself with strings, xxd and a wrong passphrase.
Two lessons ago you created a local vault and were told the file is encrypted with a passphrase-derived key. That is a claim about a file on your disk, and claims about files on your disk can be checked rather than believed.
This uses openssl on a file you create, so it works whether or not you set up a local vault. The construction is the same shape: a passphrase, a key derivation, and an authenticated cipher.
- 1
Write a plaintext env file and encrypt it with a passphrase.
bashmkdir -p /tmp/vlab && cd /tmp/vlab printf 'STRIPE_KEY=sk-live-9f2a\nDB_PASSWORD=prod-pw-42\n' > plain.env openssl enc -aes-256-cbc -pbkdf2 -iter 200000 \ -pass pass:correct-horse -in plain.env -out vault.enc - 2
Look at the plaintext file the way anyone with read access would.
bashstrings plain.env - 3
Now do the same to the encrypted one. Count the matches rather than trusting your eyes.
bashstrings vault.enc | grep -c 'sk-live\|prod-pw' # -> 0 - 4
Look at the actual bytes. The Salted__ header is the only thing that is not ciphertext.
bashxxd vault.enc | head -2 - 5
Try a wrong passphrase. It fails rather than returning garbage, because the padding check catches it.
bashopenssl enc -d -aes-256-cbc -pbkdf2 -iter 200000 \ -pass pass:wrong-guess -in vault.enc # -> bad decrypt - 6
And the right one.
bashopenssl enc -d -aes-256-cbc -pbkdf2 -iter 200000 \ -pass pass:correct-horse -in vault.enc - 7
Last check, and the one worth remembering. Encrypt the same file a second time with the same passphrase, then compare the two results.
bashopenssl enc -aes-256-cbc -pbkdf2 -iter 200000 \ -pass pass:correct-horse -in plain.env -out vault2.enc shasum -a 256 vault.enc vault2.enc cmp vault.enc vault2.enc - 8
Same content, same passphrase, completely different bytes.
text<hash-a> vault.enc <hash-b> vault2.enc vault.enc vault2.enc differ: char 9, line 1 - 9
Clean up.
bashcd /tmp && rm -rf vlab
The random salt is what makes those bytes differ, and the consequence is larger than it looks. Two vaults holding identical content are indistinguishable from two vaults holding nothing in common, so an observer who can see your encrypted files cannot tell which of them are copies, which have changed, or which two users share a password.
The reverse is the failure to recognise: a format that produces identical ciphertext for identical input is leaking equality. That is how ECB-mode images famously stay legible, and it is why deterministic encryption is a deliberate trade rather than a default.
Three things worth taking from a check that took two minutes.
- The salt is stored in the clear at the start of the file, and it is not secret. It exists so that the same passphrase produces a different key for every file, which is what stops one precomputed table from opening all of them.
- The iteration count is the cost you pay on every unlock and the cost an attacker pays on every guess. 200000 here, and a deliberately large scrypt work factor in the real local vault, are the same idea.
- A wrong passphrase produced an error rather than plausible-looking wrong output. That is the integrity check doing its job, and it is why authenticated modes are preferred.
None of this protects a file someone can read while it is unlocked, or a passphrase in your shell history. Encryption at rest answers exactly one question: what an attacker gets from the file alone.
Check your understanding
3 questionsThe salt sits in the clear at the start of the file. Is that a weakness?
What is the iteration count actually buying?
A wrong passphrase produced 'bad decrypt' rather than garbage. Why does that matter?