Encryption keeps data secret but does not stop someone changing it. If you build your own scheme from a block cipher mode plus a MAC, authenticate the ciphertext and check the MAC before decrypting: encrypt-then-MAC. Better still, use an AEAD cipher, which does this correctly for you.
Why encryption alone is not enough
With modes like CBC or CTR, an attacker who cannot read your data may still flip bits in the ciphertext and cause predictable changes in the plaintext. Without authentication, the receiver decrypts the altered data and acts on it.
OWASP's storage guidance says authenticated modes should always be used where available, and that if you must use CTR or CBC, you should add separate authentication such as Encrypt-then-MAC.
What a padding oracle is
CBC needs padding to fill the last block. If a server reveals, through an error message, a status code or even timing, whether the padding was valid after decryption, an attacker can submit modified ciphertexts and learn the plaintext piece by piece. The server becomes an oracle answering "is this padding right?"
The root cause is processing ciphertext that was never authenticated. Check the MAC first and reject bad input before any decryption or padding check happens. Then there is nothing to probe.
The three orderings
- Encrypt-then-MAC: encrypt, then MAC the ciphertext, verify MAC before decrypting. The sound choice.
- MAC-then-encrypt: MAC the plaintext, then encrypt both. Decryption happens before verification, which is where oracles appear.
- Encrypt-and-MAC: MAC the plaintext and send beside the ciphertext. The tag can leak information about the plaintext.
Use separate keys for the cipher and the MAC. See one key, one purpose.
Practical advice
- Reach for an AEAD such as AES-GCM or ChaCha20-Poly1305. See the AEAD decision guide.
- If you maintain legacy CBC code, verify the MAC with a constant-time compare before decrypting.
- Return one generic error for every failure, with similar timing.
- Do not decrypt untrusted data in a path that logs detailed errors to the client.
- Add tests that flip single bits and expect rejection.
Try it yourself
Click through it at your own pace, or open it full screen. Look at how each result names the exact place to fix.
Quick check
Does GCM have padding oracles?
GCM does not use block padding. It does authenticate the data, so altered ciphertext is rejected before plaintext is released.
Is HTTPS enough so I do not need this?
TLS protects data in transit. Data you store, or pass through queues and cookies, still needs authenticated encryption of its own.
Sources
Last reviewed: 19 October 2026.