By Freddy ·
“Encryption at rest” gets treated as a checkbox: turn on disk encryption, tick the compliance form, move on. In practice, disk encryption solves a much narrower problem than most people assume, and the gap between what it protects and what a real breach exposes is where a lot of the damage happens. If you store passwords, tokens, personal records, or anything else you wouldn't want in a leaked database dump, it's worth understanding exactly what “at rest” covers and what it doesn't.
Data protection is usually described in three states, and each one needs a different defense:
“In transit” gets the most attention because TLS is easy to enforce and easy to verify: browsers flag a missing padlock, scanners flag an expired certificate. “At rest” is where teams get vague. Ticking “encryption at rest” on a compliance questionnaire usually just means the cloud provider's default disk or volume encryption is switched on, which is a real control, but a much weaker one than the phrase implies.
Full-disk or database-volume encryption (things like AWS EBS encryption, Azure Storage Service Encryption, or Transparent Data Encryption in Postgres and SQL Server) protects the physical medium. If someone steals the drive out of a rack, or if a cloud provider's storage backend is improperly decommissioned, the raw bytes are unreadable without the volume key.
What it doesn't protect against is almost every realistic breach scenario: a stolen database credential, a SQL injection vulnerability, an over-permissioned backup that got copied somewhere it shouldn't have, or an insider with legitimate query access. In every one of those cases, the database engine itself decrypts the volume transparently and hands back plaintext rows, because that's exactly what disk encryption is designed to do for any authenticated connection. The attacker doesn't need the disk key. They just need a working connection string.
Application-level (sometimes called field-level) encryption moves the encryption step earlier: the application encrypts a specific value, using a key the database itself never has access to, before that value is ever written to a row. What lands in storage is ciphertext, and it stays ciphertext regardless of how someone gets access to the underlying data, whether that's a compromised credential, a leaked backup, or a misconfigured replica.
The key difference is who holds the key. With disk encryption, the database process holds it (or fetches it automatically), so anything that can talk to the database can read plaintext. With application-level encryption, the key lives somewhere the database itself never touches, typically a secrets manager or a dedicated key management service, so a compromised database credential alone isn't enough to read the protected fields.
Application-level encryption doesn't mean encrypting every column. That adds overhead and complexity for no benefit on data that isn't sensitive. It's worth reserving for specific fields: government ID numbers, payment details you're allowed to store, health information, private messages, API secrets belonging to your users. A few things matter for doing it correctly:
For encrypting data your own system will later decrypt, a symmetric cipher like AES is almost always the right tool: it's fast, and both encrypting and decrypting use the same key, which is fine when your application controls both ends. Asymmetric encryption earns its keep when a third party needs to encrypt something only you can decrypt, without ever holding your private key. Our AES vs RSA comparison goes through that decision in more detail.
The Encrypt / Decrypt tool on this site is a fast way to see how AES-256-GCM behaves before wiring it into an application: encrypt a value, decrypt it back, then try changing a single character of the ciphertext and watch decryption fail outright instead of returning corrupted data. That's the authentication guarantee in action, and it's the same guarantee you want the field-level encryption in your own system to give you. Everything runs in your browser through the Web Crypto API, so nothing you test with is sent anywhere.