June 11, 2026
“We Encrypt With AES-256” Is Not a Security Architecture
The questions nobody asks — and the attacks that follow when they don’t
Kalyan Dev
7 min read
You're in a security review. An engineer pulls up the architecture diagram and says the words:
"All data at rest is encrypted with AES-256."
The room nods. The checkbox gets checked. The conversation moves on.
Here's the problem: that sentence tells you almost nothing. It doesn't tell you the mode of operation. It doesn't tell you how the IV is generated, stored, or whether it's ever reused. It doesn't tell you where the key lives, who holds it, or whether the ciphertext is authenticated at all.
AES is an ingredient. A powerful one — but an ingredient. What you do with it determines whether your data is protected or just scrambled in a way that feels protected.
This post breaks down the most common AES misuses, how each one is exploited, and what good actually looks like. No PhD required.
The ECB problem: patterns don't disappear, they just look different
AES-ECB (Electronic Codebook) is the simplest mode of operation. Take your plaintext, split it into 16-byte blocks, encrypt each block independently with the key. Done.
The flaw is right there in the description: each block is encrypted independently. That means identical plaintext blocks always produce identical ciphertext blocks — regardless of where they appear in the message.
The result is that structural patterns in your data survive encryption.
The canonical demonstration of this is the "ECB penguin." Take a bitmap image of Tux the Linux mascot, encrypt it with AES-ECB, and look at the output. The silhouette is still there. The penguin is still recognizable. Because the large uniform regions of the image — all those identical pixel blocks — all encrypt to the same ciphertext. The shape leaks through.
In real systems, this matters because:
- Repeated field values are detectable. If you're encrypting database rows and a
rolefield contains"ADMIN"or"USER"across millions of records, an attacker observing ciphertext can cluster records by role without ever breaking the key. - Block substitution attacks are trivial. An attacker who intercepts ciphertext can copy a block from one record and paste it into another — swapping an encrypted "USER" block for an encrypted "ADMIN" block without needing to decrypt anything.
- Replay attacks work out of the box. Because there's no chaining or context between blocks, replaying a valid ciphertext message is straightforward.
ECB is not a theoretical concern. It's been found in production payment systems, license validation schemes, and authentication tokens — all places where "we encrypt it with AES" was considered sufficient.
CBC fixes block repetition — and introduces two new problems
AES-CBC (Cipher Block Chaining) solves the ECB problem by XORing each plaintext block with the previous ciphertext block before encrypting. Now identical plaintext blocks produce different ciphertext, because the context of every block depends on everything before it.
The chain has to start somewhere, though. The first block gets XORed with an Initialization Vector (IV) — a random value that seeds the chain.
This is where things start to go wrong in practice.
Problem 1: IV reuse
The IV must be unique per encryption operation. If you encrypt two different messages under the same key and the same IV, an attacker who obtains both ciphertexts can XOR them together and cancel out the key's contribution — leaking information about the relationship between the two plaintexts.
In TLS 1.0, the IV for each record was predictable: it was the last block of the previous record. An attacker who could inject chosen plaintext into the connection (the BEAST attack, 2011) could recover plaintext byte by byte, by exploiting the fact that the IV was known in advance.
The fix sounds simple: generate a fresh random IV for every message. In practice, this gets broken by:
- IVs hardcoded in config files
- IVs derived from predictable sources (timestamps, counters)
- IVs stored alongside the key instead of with the ciphertext
- Developers reusing the same IV "for performance"
Problem 2: No integrity
This is the deeper issue.
CBC gives you confidentiality. It does not give you integrity. The ciphertext is not authenticated — there's nothing preventing an attacker from flipping bits in the ciphertext and producing modified plaintext on decryption.
This enables padding oracle attacks.
Here's the core idea. AES-CBC decryption requires the plaintext to be padded to a multiple of 16 bytes (PKCS#7 padding). When decryption finishes, the padding is checked and stripped. If the padding is invalid, most implementations throw an error — or respond differently than they do for valid padding.
That difference in behavior is an oracle. An attacker who can submit ciphertext and observe whether the padding was valid can recover the plaintext one byte at a time, by systematically flipping ciphertext bits and watching the server's response.
No key needed. Just the ability to submit ciphertext and observe the outcome.
This isn't theoretical. POODLE (2014) exploited this against SSL 3.0. Lucky13 (2013) exploited it against TLS by measuring timing differences in the error path — even when the error message was identical. The oracle was in the clock.
The fix exists and has been known for decades: authenticate your ciphertext. If the integrity check fails, reject the message before decryption ever happens. An attacker who can't get valid padding error signals can't run the attack.
But if you're using raw CBC with no MAC, you're vulnerable regardless of your key length.
The key management problem nobody talks about
Even if you use AES-GCM (correct) with proper nonce generation (correct), there's a third failure mode that sits entirely outside the cipher itself: key management.
Ask these questions about any system that "encrypts with AES-256":
Where does the key live? If the answer is "next to the data it encrypts" — in the same database, the same S3 bucket, the same config file — the key provides almost no protection. An attacker who exfiltrates the data also gets the key.
What happens when the key leaks? If there's no rotation policy, a leaked key exposes every piece of data ever encrypted with it. Retroactively. The breach scope is unbounded.
Who can access the key at runtime? If the application process can read the key at any time, then any code execution vulnerability in that application — any of them — gives an attacker the key. Hardware-backed key storage (HSMs, TPMs, cloud KMS with strict IAM policies) exists precisely to limit this.
Is the key derived properly? If you're deriving a key from a password or a seed value, the derivation function matters enormously. Using a fast hash (SHA-256, MD5) as a KDF is wrong. HKDF, PBKDF2, Argon2, or scrypt exist for this reason.
When I built HSM firmware from scratch for MITRE eCTF this year — bare-metal on a Cortex-M0+, no OS, no library support — key management was the first design decision, not an afterthought. We used HKDF for key derivation, session-based ephemeral keys, and HMAC-SHA256 for authentication on every message. Not because it was required. Because without it, the AES we'd implemented would have been theater.
What good actually looks like
The modern standard for symmetric encryption is authenticated encryption with associated data (AEAD). The two primary options:
AES-GCM (Galois/Counter Mode) Combines AES in counter mode (confidentiality) with a GHASH-based authentication tag (integrity). Encrypting and authenticating happen in a single pass. The authentication tag covers both the ciphertext and any associated data you want to authenticate without encrypting (headers, metadata, record IDs).
Critical constraint: the nonce must be unique per (key, message) pair. AES-GCM with a repeated nonce under the same key is catastrophically broken — an attacker can recover the authentication key and forge arbitrary ciphertext. Use a 96-bit random nonce from a CSPRNG, or a counter managed carefully.
ChaCha20-Poly1305 An alternative AEAD construction that doesn't rely on hardware AES acceleration. Preferred on platforms where constant-time AES isn't guaranteed (some embedded systems, older mobile hardware). Nonce reuse is still catastrophic, same as GCM.
Beyond the cipher choice, a minimal checklist:
- Generate nonces/IVs with a CSPRNG. Never hardcode, never reuse, never derive from a timestamp.
- Store keys separately from the data they protect. Use a KMS or HSM if the threat model warrants it.
- Rotate keys on a defined schedule, and have a documented procedure for emergency rotation on suspected compromise.
- Verify authentication tags before any other processing. Reject first, then decrypt.
- Use a proper KDF if deriving keys from passwords or seeds. HKDF for key material, Argon2id for password hashing.
The audit question that catches this
If you're reviewing a system and you want to know whether the encryption is real or compliance theater, ask one question:
"What happens if an attacker can submit arbitrary ciphertext to the decryption endpoint?"
If the answer is "it would just fail" — probe that. How does it fail? Does it fail before or after decryption? Does the error message or timing leak any information about the internal state?
A system that uses AEAD and verifies the authentication tag first will give you a clean "invalid tag, rejected" before decryption even runs. A system using unauthenticated CBC will decrypt the ciphertext, check the padding, and potentially leak information through the error path — regardless of key length.
"We use AES-256" doesn't answer this question. The mode does.
The real takeaway
Key length is almost never the weakest link in a real-world encryption failure. 128-bit AES has never been broken. The breaks happen in the plumbing around it: the mode, the IV handling, the authentication layer, the key storage.
When someone tells you a system is secure because it "uses AES-256," the right response isn't to nod. It's to ask the four questions from the top:
ECB or CBC? Fixed IV? Who holds the key? Is the ciphertext authenticated?
The answers are where security lives.
This is part of Vulnerability Decoded — a series breaking down real cryptographic and software vulnerabilities, attack paths, and what defenders should actually do about them. If you work in security, build systems that handle sensitive data, or you're studying for a role in either — follow along.
I'm a grad student in cybersecurity at Northeastern University, where I build things that have to be secure by design: HSM firmware, embedded systems, and the occasional CTF challenge that turns into a post like this one.