Cryptographic goals
Threat-model encrypted data
Decide which attacker, endpoint, storage system, administrator, or physical loss encryption should protect against.
“We encrypt the data” means nothing on its own. Ask two questions first: who has the key, and who are we trying to keep out?
The answers decide where encryption belongs. Each layer blocks a different attacker, and no layer blocks all of them.
What each layer protects
Disk encryption protects a powered-off stolen disk. It does nothing against an attacker who controls the running server. The operating system decrypts transparently for every process, including the attacker’s.
Database-level or application-level encryption protects a database dump or a stolen backup. But the application holds the key, so the application can still decrypt every record. Anyone who compromises the application inherits that ability.
TLS protects data moving between machines. It says nothing about what happens at either end.
The most common mistake I see is an encrypted database field with the decryption key sitting in the same environment as the application:
# .env on the same host that stores the data
DATABASE_URL=postgres://[email protected]/prod
FIELD_ENCRYPTION_KEY=6f1d...c2a9
A stolen database backup is protected. Remote code execution in the application is not. The attacker reads the key from the environment and decrypts everything, same as the app does.
Map plaintext and keys before choosing
Before you pick a layer, trace one piece of data through the system. Mark every place where plaintext or key material exists:
customer address:
browser input -> plaintext (TLS in transit)
app server memory -> plaintext
request logs -> plaintext? <- often forgotten
database column -> ciphertext
database backup -> ciphertext
encryption key -> app environment variable
The logs line is where audits find surprises. Encrypting the column while logging the request body protects nothing. The plaintext is still on disk, just in a different file.
Moving the key to a managed key service helps. The key is no longer in the environment, and you get an audit log of every decrypt call. But an authorized application can still request decryption whenever it wants.
Encryption changes the attack path. It does not remove it. That is not a weakness, it is an honest description of what you bought. Once you know which attacker each layer stops, you can decide whether that attacker is the one you care about.
Try this on your own project: draw where plaintext and keys live for one customer address, from browser input through logs, database, backups, and application memory. Mark which attacker each encryption layer blocks. Then assume the application process is compromised and list every plaintext and decryption path that remains. If the diagram shows the key next to the data, you found your first fix.
Lesson completed