Articles

What Is a Padding Oracle Attack?

A padding oracle attack exploits error messages during decryption to recover plaintext byte by byte, without ever breaking the cipher itself.

Chisato Chisato · · 5 min read
An unlocked padlock resting on a keyboard

A padding oracle attack is a cryptographic exploit that recovers plaintext from a block cipher, not by breaking the encryption algorithm, but by abusing how a server responds when decrypted padding is invalid. If an attacker can distinguish a “padding error” response from any other kind of failure, they can decrypt an entire ciphertext byte by byte — without ever knowing the key.

It’s a favorite example in security courses because it shows that a cipher can be mathematically sound and still be broken by an implementation detail: an error message that reveals slightly too much.

Why padding exists

Block ciphers like AES operate on fixed-size blocks — 16 bytes for AES. Real messages rarely land on an exact multiple of the block size, so before encryption, the plaintext is padded out to fill the final block. A common scheme, PKCS#7, pads with bytes whose value equals the number of padding bytes added: if 4 bytes of padding are needed, each of those 4 bytes has the value 0x04.

After decryption, the receiver checks the last byte, reads how many padding bytes it claims, and verifies that many bytes at the end all match that value. If they don’t, the receiver rejects the message as having invalid padding.

That check — “is the padding valid or not?” — is the oracle an attacker exploits.

How the attack works

Padding oracle attacks specifically target CBC (cipher block chaining) mode, where each plaintext block is XORed with the previous ciphertext block before encryption. To decrypt, the receiver does the reverse: decrypt the ciphertext block, then XOR the result with the previous ciphertext block to recover the plaintext.

That XOR step is the leverage point. An attacker who can intercept ciphertext and resend modified versions to a server — one that dutifully reports “padding is invalid” as a distinguishable response, whether via an explicit error, an HTTP status code, or even just a timing difference — can manipulate the previous ciphertext block byte by byte and observe whether the resulting “decrypted” padding comes out valid.

The attack works backward from the last byte of a block:

  1. Flip bits in the last byte of the (attacker-controlled, previous) ciphertext block and resend.
  2. The server decrypts and checks padding. If flipping produces a byte that decrypts to 0x01 (valid single-byte padding), the server accepts it; otherwise it rejects.
  3. That single bit of information — accept or reject — tells the attacker the value of one intermediate byte, which combined with the known ciphertext, reveals one byte of the real plaintext.
  4. Repeat for each byte position, moving to two-byte padding (0x02 0x02), then three, and so on, working through the entire block.

At most 256 guesses per byte are needed (worst case), and often far fewer. Doing this for every byte of every block fully decrypts the message — no key required, purely by asking “valid padding or not?” over and over.

Real-world instances

This isn’t a theoretical concern. POODLE (2014) exploited padding oracle weaknesses in SSL 3.0’s CBC mode. Lucky Thirteen (2013) used timing differences in TLS’s padding checks as the oracle, even when explicit error messages were suppressed. Padding oracle bugs have also turned up repeatedly in web application frameworks that used CBC-encrypted, unauthenticated cookies or view-state tokens — an attacker could use the framework’s own decryption error behavior as the oracle to forge arbitrary values.

The common thread: any system decrypting attacker-influenced ciphertext, and behaving even slightly differently based on padding validity, is a candidate. The “different behavior” doesn’t need to be an explicit error string — a different HTTP status code, a longer processing time, or even a different response size can all serve as the oracle if measured carefully.

How to prevent it

Use authenticated encryption. Modes like AES-GCM combine encryption with a built-in integrity check (a MAC), so tampered ciphertext is rejected as inauthentic, before padding is ever examined. This is the single most effective fix: an authenticated mode gives the attacker no padding-related signal to probe, because the message fails a completely different, single check first. This is one of the reasons AEAD (authenticated encryption with associated data) modes have replaced plain CBC as the modern default.

If you must use CBC, use encrypt-then-MAC. Verify a message authentication code over the ciphertext before attempting decryption or checking padding. An invalid MAC is rejected uniformly, so an attacker never gets to the padding-check step at all. Never decrypt first and check integrity second — that ordering is exactly what makes padding oracle attacks possible. This is conceptually the same lesson as validating a JWT’s signature before trusting anything in its payload.

Respond uniformly to failures. If a system does perform separate decryption and validation steps, it should return the same generic error, in the same amount of time, regardless of why decryption failed. Any observable difference — error text, HTTP status, or response latency — can become an oracle, which is the same underlying lesson as timing attacks more generally: constant-time comparison isn’t just about MACs and passwords, it’s about any comparison an attacker can trigger repeatedly.

Padding oracle attacks and encryption in general

It’s worth being precise about what this attack does and doesn’t break. It doesn’t recover the encryption key, and it doesn’t work against the cipher’s core mathematics — symmetric encryption like AES remains sound. It exploits a side channel in how a specific implementation handles errors, which is a broader theme in applied cryptography: correctness of the primitive and correctness of the protocol around it are two different problems, and most real-world breaks target the second one. The same distinction shows up in the difference between hashing and encryption — using the wrong primitive, or the right primitive with a leaky implementation, both lead to the same kind of failure even though the cipher itself was never “broken.”

The takeaway

A padding oracle attack decrypts CBC-encrypted ciphertext without the key, by repeatedly submitting modified ciphertext and observing whether the server’s response indicates valid or invalid padding — a signal that, byte by byte, reveals the plaintext. It’s a reminder that error handling is part of a cryptographic system’s attack surface, not a detail outside it. The fix isn’t a smarter padding scheme; it’s authenticated encryption, or at minimum verifying integrity before ever touching padding, so there’s no oracle left to query.

Chisato Chisato · · 4 min read

What Is a Timing Attack? Side-Channel Leaks Explained

A timing attack infers secret data by measuring how long an operation takes to run. How timing side channels leak information and how to close them.

#Security #Cryptography #Web Development
Chisato Chisato · · 4 min read

Trusted Types API: Stopping DOM XSS at the Sink

The Trusted Types API blocks DOM-based XSS by forcing risky DOM sinks like innerHTML to accept only vetted objects instead of raw strings.

#Security #Web Development #JavaScript