system-design

Encryption in Transit and at Rest

Scrambling data so that stealing it isn’t enough to read it. Two places it matters — while it moves and while it sits — and the part everyone gets wrong is key management.

Prerequisites: HTTP & TLS, AuthN vs AuthZ Time to read: ~20 minutes


The two places encryption matters

Data exists in two states, and each needs protecting:

🚨 You need both. TLS protects the wire but the database stores plaintext; disk encryption protects the disk but the network is exposed. A breach can come from either direction, and encrypting one without the other leaves an open door.

(There’s a third, harder state — in use, data in memory while being processed — addressed by confidential computing / enclaves, still niche. Worth knowing the term.)


Symmetric vs asymmetric: the foundation

Two kinds of encryption, and knowing the difference is fundamental.

Symmetric — one key encrypts and decrypts. Fast.

AES-256:  encrypt(plaintext, key) → ciphertext
          decrypt(ciphertext, key) → plaintext     (same key)

✅ Very fast (hardware-accelerated). Used for the actual bulk data. ❌ 🚨 The key distribution problem: both sides need the same secret key, so how do you share it securely over an insecure network? You can’t just send it — anyone watching gets it too.

Asymmetric (public-key) — a pair: a public key encrypts, a private key decrypts (or vice versa for signing). Slow.

RSA / ECDSA:  encrypt(plaintext, PUBLIC key) → ciphertext
              decrypt(ciphertext, PRIVATE key) → plaintext

Solves key distribution — publish the public key freely; only the private-key holder can decrypt. Also enables digital signatures (sign with private, anyone verifies with public). ❌ Slow — impractical for bulk data.

🚨 The insight that makes TLS work: use both. Asymmetric to exchange a symmetric key securely, then symmetric for the actual data. You get asymmetric’s safe key distribution and symmetric’s speed.

1. Asymmetric key exchange establishes a shared symmetric "session key"  (slow, once)
2. Symmetric encryption with that session key for all the data           (fast, ongoing)

This hybrid is exactly what the TLS handshake does.


Encryption in transit: TLS

Covered in depth in HTTP & TLS; the security essentials:

TLS gives you three things (not just secrecy):

  1. Confidentiality — nobody on the path can read it.
  2. Integrity — nobody can modify it undetected.
  3. Authentication — you’re talking to who you think (via certificates).

🚨 Authentication is the part people forget. Encryption without authentication means you’ve securely connected to an attacker — a man-in-the-middle. Certificates, signed by a trusted CA, are what prove the server is who it claims. Encryption alone isn’t enough.

Practical requirements:

mTLS (mutual TLS) — both sides present certificates, so the client is authenticated too. Standard for service-to-service auth inside a mesh, and a strong answer to “how do services authenticate each other?”


Encryption at rest

Protecting stored data. Several layers, and they compose:

1. Full-disk / volume encryption. The whole disk is encrypted (LUKS, BitLocker, cloud EBS encryption). ✅ Transparent, protects against stolen disks and improperly-wiped hardware. ❌ 🚨 Doesn’t protect against much — if an attacker has access to the running system (SQL injection, a compromised app), the OS decrypts transparently and they read plaintext. Disk encryption protects the physical medium, not a live application breach.

2. Database-level (Transparent Data Encryption, TDE). The database encrypts its files. Same limitation — transparent to authenticated queries, so an app-level breach still reads plaintext. Protects backups and stolen files.

3. Application-level / field-level encryption. 🚨 The strongest, and the one that matters most for sensitive data. The application encrypts specific fields before storing them, so the database only ever sees ciphertext.

# The database stores ciphertext; a DB breach reveals nothing
encrypted_ssn = encrypt(user.ssn, field_key)
db.save(encrypted_ssn)

✅ Protects even against a full database compromise, a malicious DBA, or a leaked backup — the data is useless without the application’s keys, which live elsewhere. ❌ Can’t index or search encrypted fields normally, more complex, and key management is critical.

🎙️ “Full-disk encryption for the baseline, but for the sensitive fields — SSNs, payment data — I’d use application-level encryption so the database only ever holds ciphertext. Disk and TDE encryption are transparent to an authenticated breach; field-level isn’t.”

This distinction — disk/TDE protects the medium, field-level protects against a live breach — is a strong interview point.


🚨 Key management: where it all actually lives or dies

Encryption is only as strong as your key management, and this is where systems fail. Encrypting data with a key stored next to the data protects nothing — the attacker who gets the data gets the key.

The essentials:

A dedicated key manager. Keys live in a KMS (AWS KMS, GCP KMS, Azure Key Vault) or an HSM (Hardware Security Module — tamper-resistant hardware where keys never leave). 🚨 The application never sees the master key. It asks the KMS to encrypt/decrypt, or to unwrap a data key.

Envelope encryption — the standard pattern, worth knowing:

1. A master key (in the KMS, never extracted) encrypts...
2. ...a data key, which encrypts the actual data.
3. Store the encrypted data + the encrypted data key together.
4. To decrypt: ask the KMS to decrypt the data key, then use it locally.

🚨 Why: the fast symmetric data key does the bulk work locally, but it’s protected by the master key that never leaves the KMS. And rotating the master key doesn’t require re-encrypting all your data — just re-wrapping the data keys.

Key rotation. Keys are rotated periodically (and immediately on suspected compromise). Envelope encryption makes this cheap.

Separation. 🚨 The keys must be stored separately from the data, with separate access controls. A leaked database backup must not also leak the keys.

Never in code or config. Keys and secrets in git, in environment files, or hardcoded are a breach. → Secrets Management


Hashing vs encryption: don’t confuse them

🚨 A common confusion, and an interview trap.

  Encryption Hashing
Reversible? ✅ Yes (with the key) ❌ No — one-way
Purpose Protect data you need to read back Verify without storing the original
Use for PII, messages, files, anything you retrieve Passwords, integrity checks, deduplication

🚨 Passwords are hashed, never encrypted — you never need to read a password back, only verify a submitted one matches. Encrypting passwords means a stolen key exposes them all; hashing (with a slow, salted algorithm like bcrypt/Argon2) means even the breach can’t reverse them. → AuthN

Encrypt data you must retrieve (SSNs, card numbers you charge again, messages). Hash data you only verify (passwords).


What to encrypt

Prioritize by sensitivity and compliance:

⚖️ Encryption isn’t free — CPU cost, latency, key-management operational burden, and encrypted fields you can’t query. But the cost of not encrypting sensitive data is a breach, a regulatory fine, and lost trust. For sensitive data the trade is clear; for a public product catalogue, encrypting it at rest adds cost for little benefit. Encrypt by sensitivity, not uniformly.


⚖️ Trade-offs

Choice Gain Cost
TLS everywhere Confidentiality, integrity, authentication Handshake latency (small with 1.3), cert management
Disk/TDE encryption Protects stolen media and backups Transparent to a live app breach — limited protection
Field-level encryption Survives a full DB compromise Can’t index/search; complexity; key management
Envelope encryption Cheap rotation; master key never leaves KMS KMS dependency and cost
mTLS internally Service-to-service authentication Certificate lifecycle to manage

In the real world


🚨 Interview traps


🎙️ Soundbites


🛠️ Try it

1. Hybrid encryption by hand. Encrypt a large file with AES (symmetric), then encrypt the AES key with RSA (asymmetric). To decrypt: RSA-decrypt the key, then AES-decrypt the file. This is TLS’s model in miniature, and building it makes the “why both” point obvious.

2. Prove disk encryption’s limitation. Set up a database with disk encryption. Then run a query as an authenticated user (or simulate a SQL injection) and confirm you read plaintext — the disk encryption did nothing. Then encrypt a field at the application level and confirm the same query now returns ciphertext.

3. Use envelope encryption. With a cloud KMS (or a local mock), generate a data key, encrypt data with it locally, and store the data key encrypted by the KMS master key. Then decrypt: ask the KMS to unwrap the data key, use it locally. Note that the master key never left the KMS.

4. See a cert. openssl s_client -connect example.com:443 — inspect the certificate chain, the expiry, and the cipher negotiated. Then check the expiry date and imagine the outage if nobody renews it.


Check yourself

1. Why does TLS use both symmetric and asymmetric encryption? To get the strengths of both while avoiding their weaknesses. Symmetric encryption (AES) is fast and ideal for bulk data, but both sides need the same secret key — and you can't safely share a secret over an insecure network where anyone might be listening (the key-distribution problem). Asymmetric encryption (RSA/ECDSA) solves distribution — you publish a public key that anyone can encrypt to, but only the private-key holder can decrypt — but it's too slow for bulk data. So TLS uses asymmetric encryption *once* to securely establish a shared symmetric session key, then uses fast symmetric encryption with that key for all the actual traffic. Safe key exchange plus fast bulk encryption.
2. Why is authentication as important as encryption in TLS? Because encryption without authentication just means you've established a secure channel to *someone* — potentially an attacker performing a man-in-the-middle. If you encrypt your traffic but don't verify who's on the other end, an attacker who intercepts the connection can present themselves as the server, decrypt everything you send, and relay it (modified or not) to the real server. TLS prevents this with certificates: the server presents a certificate signed by a Certificate Authority your system already trusts, cryptographically proving it controls the domain you intended to reach. Encryption keeps the data secret; authentication ensures the secret is shared with the right party. Both are required.
3. Why does disk encryption provide limited protection against a real breach? Because it's transparent to authenticated access. Full-disk and transparent database encryption decrypt data automatically for the running system, so anyone or anything that has legitimate access to the live application — a SQL injection attacker, a compromised application server, a malicious insider, a stolen credential — reads plaintext, because the OS or database hands it over decrypted. Disk encryption protects only the *physical medium*: a stolen laptop, a decommissioned drive, a raw disk image. That's worth having (it's cheap and defends against improper disposal and physical theft), but most data breaches happen through the application, not by stealing hardware, so for sensitive data you need application-level field encryption where the database only ever holds ciphertext.
4. What is envelope encryption and why is it used? A pattern where a master key encrypts a data key, and the data key encrypts the actual data. The master key lives in a KMS or HSM and *never leaves it*; the data key does the fast bulk encryption locally, and is stored (encrypted by the master key) alongside the ciphertext. To decrypt, the application asks the KMS to decrypt the data key, then uses it locally. It's used because it combines security and practicality: the master key stays in tamper-resistant, access-controlled hardware and is never exposed to the application, while the performance-sensitive bulk encryption happens locally with the data key. It also makes key rotation cheap — rotating the master key only requires re-wrapping the (small) data keys, not re-encrypting all the (large) data.
5. Why are passwords hashed rather than encrypted? Because you never need to read a password back — you only need to verify that a submitted password matches the stored one, which hashing does by hashing the submission and comparing. Encryption is reversible by design: it exists so you can recover the plaintext, which requires a key, and if that key is compromised (and in a breach that exposes the password store, the key is often reachable too), every password is instantly recovered. Hashing is one-way — there's no key and no reversal — so even a full breach of the hash store doesn't hand over the passwords, provided you used a slow, salted, memory-hard algorithm (bcrypt, scrypt, Argon2) that makes brute-forcing each hash prohibitively expensive. The rule: encrypt data you must retrieve; hash data you only need to verify.

Further reading