46 min read

They Read Your Plaintext Without Breaking Your Cipher: A Field Guide to Padding Oracles

A padding oracle reads your plaintext without touching your key. Why CBC, Vaudenay, Lucky13, and POODLE are one bug -- and why Encrypt-then-MAC ends it.

Permalink

1. The Break That Never Touched the Key

In September 2010, two researchers pointed a few thousand malformed requests at an ordinary ASP.NET application and walked away with its authentication tickets and its web.config -- the site's master secrets -- without ever attacking its AES-256 encryption or going near the key [1]. It had done nothing more than return one error when a decrypted request had bad padding and a different error when the padding was fine but the data was not. That one-bit difference is a decryption machine: the attackers never broke the cipher, they turned the server's error handling into the decryption function.

Sit with the paradox, because the rest of this article lives inside it. AES-256 was, and is, intact. Nobody factored anything, guessed a key, or exploited a weakness in the block cipher's rounds. The plaintext walked out the door one byte at a time purely because the server answered a question it should never have been willing to answer: was this ciphertext, which I did not create, well-formed after I decrypted it?

Juliano Rizzo and Thai Duong automated that question against WebResource.axd and ScriptResource.axd handlers and recovered Forms-authentication tickets and server files. The United States National Vulnerability Database catalogs it as CVE-2010-3332, the "ASP.NET Padding Oracle Vulnerability," and Microsoft shipped an out-of-band patch to close it [2]. This was not a lab curiosity. It was, at the time, the largest deployed instance of a bug that had been sitting in plain sight for eight years.

Padding oracle

A padding oracle is an attack class, not a cipher weakness. A receiver decrypts an attacker-chosen ciphertext, checks whether the recovered plaintext has valid padding, and reveals the verdict -- through an error message, a timing difference, or any observable behavior. That single accept/reject bit, repeated, becomes a plaintext-recovery oracle that never attacks the underlying cipher or key.

The reframe is worth stating in the sharpest possible terms, because once you hold it you will see this bug everywhere.

The attacker never attacks the cipher. They turn the receiver's error handling into the decryption function -- "decrypt, then validate" becomes "decrypt, then confess."

This is Part 6 of a field guide for protocol designers. It assumes the malleability of CBC mode from Part 5 (the lever the attack pulls) and the chosen-ciphertext adversary from Part 1 (this is that adversary, deployed and winning). By the end you will carry one diagnostic sentence and drop every named break in this article -- Vaudenay, Lucky Thirteen, POODLE, and tomorrow's application-layer oracle -- onto it on sight: when a ciphertext you did not create arrives, what does the receiver reveal about it before it has proven the ciphertext authentic?

If AES was intact and the key was never touched, then the machine that recovered the plaintext was built entirely out of the server's own responses. That idea did not begin with CBC, or even with symmetric cryptography. To see the atom clearly, go back to the first time anyone realized that a receiver's verdict on validity is itself a decryption function.

2. A Validity Check Is a Decryption Oracle

The year is 1998, the place is Bell Labs, and Daniel Bleichenbacher is staring at an SSL server that is far too honest. When the server receives an RSA-encrypted message, it decrypts and checks whether the result has the structure that the PKCS #1 v1.5 standard demands. If the structure is wrong, it says so. Bleichenbacher realized that this single yes/no answer -- does the decrypted message have valid PKCS #1 formatting? -- is enough to recover the plaintext.

With roughly 2202^{20} adaptively chosen queries, he could extract an RSA-protected session key without factoring the modulus and without ever learning the private key [3]. The server's "is this well-formed?" answer was the attacker's decryption function. This is the seed of everything that follows.

It is also the moment to nail down a boundary that expert readers will check for. Bleichenbacher's attack is an RSA PKCS #1 v1.5 oracle -- a different primitive with different arithmetic. It is the conceptual ancestor of the CBC padding-oracle class "in spirit," but it is never itself a CBC padding oracle [3]. Calling Bleichenbacher "the first CBC padding oracle" is a common and consequential error. His target is RSA public-key encryption; the CBC class is symmetric. The shared idea -- a structural validity verdict on attacker-chosen ciphertext leaks the plaintext -- is what makes him the ancestor, not the mechanism. The distinction matters because the family tree has two branches, and confusing them muddles the fix.

Chosen-ciphertext attack (IND-CCA2)

In an adaptive chosen-ciphertext attack, the adversary submits ciphertexts of its choosing and learns something from the receiver's reaction to each one. A scheme is IND-CCA2 secure only if those reactions leak nothing that helps distinguish or recover plaintext. A padding oracle is precisely a receiver whose reaction leaks -- so it is a live, deployed IND-CCA2 break, the security notion this series defines in Part 1.

The whole subject lives inside that definition. If a receiver's response to a ciphertext it did not create tells the attacker anything, the game is already lost -- the only question is how expensive the win is.

Four years after Bleichenbacher, Serge Vaudenay carried the idea across the aisle from public-key to symmetric cryptography. In "Security Flaws Induced by CBC Padding," presented at EUROCRYPT 2002, he showed that a receiver which decrypts a CBC ciphertext and reveals whether the padding is valid becomes a plaintext-recovery oracle against SSL, IPSEC, and WTLS [7]. This is the founding result of the class -- the moment "confidentiality without integrity" was shown to be, in the general case, a decryption oracle waiting to be asked.

The object it manipulates had arrived on schedule: PKCS #7 padding, the append-n-bytes-each-equal-to-n scheme, was standardized as RFC 2315 in the same era, so the manipulable surface and the attack template appeared together [8]. Vaudenay's procedure is documented in cryptography's standard texts as the canonical illustration of chosen-ciphertext insecurity [9].

Ctrl + scroll to zoom
The padding-oracle lineage, 1998 to 2022: one idea leaking through progressively quieter channels, closed only by inverting the order.

Vaudenay's real contribution was noticing that CBC makes the symmetric version cleaner than Bleichenbacher's RSA original: flip a byte, watch the verdict, recover a byte. To believe that -- and to earn the right to use it on every later break -- you have to see the arithmetic. It is simpler than it sounds, and once you see it you cannot un-see it in any decrypt-then-check system you review.

3. The Atom: Vaudenay's Oracle, Worked Byte by Byte

Everything in this article is one move, performed once here and then reused by every named break with only the channel changed. Derive it in full and the rest of the piece can move fast. There are three steps: understand the padding, understand the lever, then watch the recovery.

Step one: three padding formats, disambiguated once

Block ciphers operate on whole blocks, so a message that does not fill the last block must be padded, and the padding must be self-describing so the receiver can strip it. PKCS #7 is the canonical scheme.

PKCS#7 padding

To pad a message to a block boundary, append n bytes, each equal to the value n. Need three bytes of padding? Append 03 03 03. Need one? Append 01. If the message is already block-aligned, append a full extra block of padding (for a 16-byte AES block, sixteen bytes of 10). To unpad, read the final byte n, verify that the last n bytes all equal n, and strip them. Standardized in RFC 2315 [8].

Here is the trap that catches expert readers, so disambiguate it now and never again: not every named attack operates on literal PKCS #7 bytes. Three formats matter, and mitigations for one do not automatically cover another.

PKCS #7 (RFC 2315) requires every pad byte to equal the pad length [8]. TLS 1.0 through 1.2 use a PKCS #7-like scheme where every pad byte equals length - 1, and -- this detail will return with Lucky Thirteen -- the record is MAC'd, then padded, then encrypted [10]. SSL 3.0 is the dangerous outlier: its pad bytes are arbitrary, and only the final length byte is constrained, so there is effectively no padding content to check at all [11].

Padding formatPad-byte ruleWhat the receiver checksBreak that targets it
PKCS #7 (RFC 2315)Append n bytes each equal to nLength byte and every pad byteVaudenay's canonical oracle
TLS 1.0 to 1.2 (RFC 5246)Every pad byte equals length - 1; MAC then pad then encryptLength and pad bytes, after decryptLucky Thirteen
SSL 3.0 (RFC 6101)Pad bytes arbitrary; only the final length byte constrainedLength byte only; pad bytes uncheckedPOODLE
The DES-era ancestor is PKCS #5, re-published as RFC 8018, which is the same scheme fixed at an 8-byte block; PKCS #7 generalizes it to any block size up to 255 [12]. The two names describe one idea, which is why you see both in old code.

That single table is why "just make the padding check uniform" was never a complete fix -- it says nothing about SSL 3.0, where there is no pad content to make uniform.

Step two: CBC malleability, the lever

CBC malleability

In CBC decryption, each plaintext block is Pi=Dk(Ci)Ci1P_i = D_k(C_i) \oplus C_{i-1}: decrypt the current ciphertext block with the block cipher, then XOR the previous ciphertext block. Because that XOR is the last step, an attacker who flips a bit in Ci1C_{i-1} deterministically flips the same bit in PiP_i -- steering the plaintext without any knowledge of the key. Part 5 derives this in full; here it is the lever the oracle pulls.

Ctrl + scroll to zoom
CBC decryption. Because the previous ciphertext block is XORed in last, flipping a byte there flips the same byte of the recovered plaintext -- the key is never involved.

Hold the two pieces together: the receiver will tell you whether a decrypted block has valid padding, and you can steer any byte of that decrypted block by editing the block in front of it. Those two facts are a decryption oracle.

Step three: the recovery, last byte first

Take a target ciphertext block CtC_t whose plaintext you want. Prepend a block CC' that you fully control, and ask the receiver to decrypt the pair. The receiver computes an intermediate block I=Dk(Ct)I = D_k(C_t) and returns the plaintext P=ICP = I \oplus C', then checks its padding. You do not know II, but you can search for it one byte at a time.

Fix your attention on the last byte. Vary CC''s last byte over all 256 possible values and watch the verdict. For exactly one value, the decrypted last byte becomes 0x01, which is valid single-byte padding, and the receiver accepts. At that instant you know that I[last]C[last]=0x01I[\text{last}] \oplus C'[\text{last}] = \texttt{0x01}, so the intermediate byte is I[last]=C[last]0x01I[\text{last}] = C'[\text{last}] \oplus \texttt{0x01}. XOR that against the real previous ciphertext byte and you have recovered a true plaintext byte -- and AES was never touched.

One honest wrinkle hides inside "about 128 guesses." A guess can occasionally produce valid longer padding by luck -- for instance if the plaintext already ended in 0x02, a crafted byte could yield 02 02. So the careful attacker perturbs the second-to-last byte to confirm the intended 01 interpretation. That rare double-check is why the average is ~128 rather than a clean 128, and why worst-case is 256.

To peel the next byte, target two-byte padding: set the known last byte to decrypt to 0x02 (you can, because you now know its intermediate), and brute-force the second-to-last byte until the receiver sees 02 02. March inward -- 03 03 03, then 04 04 04 04 -- and a full block falls. The cost is about 128 guesses per byte on average and 256 in the worst case [7].

Ctrl + scroll to zoom
Vaudenay's last-byte recovery loop. The attacker varies one controlled byte until the padding check accepts; that single accept leaks one intermediate byte.

Run the atom yourself. The simulation below models the oracle over a fixed intermediate block -- no real cipher, so you can watch the one-in-256 accept that leaks a byte.

JavaScript Vaudenay's last-byte recovery, simulated
// A toy padding oracle over a fixed 16-byte "intermediate" block.
// No real crypto: we model I = D_k(C_t) as a known array so you can watch
// the 1-in-256 accept that leaks one plaintext byte.
const BLOCK = 16;

// Pretend these are the secret intermediate bytes I = D_k(C_t).
const intermediate = [0x8a,0x2f,0x11,0x77,0x4c,0x93,0xe0,0x05,
                    0xbb,0x6d,0x19,0xa4,0x3c,0xf8,0x52,0x7e];
// The real previous-ciphertext block (known to the attacker).
const realPrev = [0x01,0x23,0x45,0x67,0x89,0xab,0xcd,0xef,
                0xfe,0xdc,0xba,0x98,0x76,0x54,0x32,0x10];

// Oracle: is the last decrypted byte valid 0x01 padding?
function oracle(cprime) {
const lastPlain = intermediate[BLOCK-1] ^ cprime[BLOCK-1];
return lastPlain === 0x01;   // the single leaked bit
}

// Attack: brute-force the last byte of a controlled block.
let guesses = 0;
const cprime = new Array(BLOCK).fill(0);
for (let g = 0; g < 256; g++) {
guesses++;
cprime[BLOCK-1] = g;
if (oracle(cprime)) {
  const intByte = g ^ 0x01;                    // I[last] = guess XOR 0x01
  const plainByte = intByte ^ realPrev[BLOCK-1];
  console.log("Accepted after " + guesses + " guesses");
  console.log("Recovered intermediate byte: 0x" + intByte.toString(16));
  console.log("Recovered plaintext byte:    0x" + plainByte.toString(16));
  break;
}
}

Press Run to execute.

One reliable accept/reject bit on attacker-chosen ciphertext, plus CBC's malleability, is a complete decryption function -- about 128 guesses per byte, and the key is never touched. Every named break in the rest of this article is that same atom, with only the channel that carries the bit changed.

That is Aha number one, and it dismantles the mental model most engineers start with. AES-256's strength was never the variable. The variable was what the receiver was willing to say about a ciphertext it had not authenticated. Before moving on, one point of vocabulary, because it changes how you diagnose the bug.

The atom needs exactly one thing from the receiver: a distinguishable answer to "was the padding valid?" Vaudenay read that answer off a loud error message. The next fifteen years are the story of the field muffling that answer, one channel at a time -- and the same bit escaping through the next-quietest one every single time.

4. One Distinguisher, Four Quieter Channels

The atom never changes. What changes is how the accept/reject bit is observed -- and each time the field silences one channel, the bit re-emerges through a quieter one. Here is the entire failure catalog as one descending staircase, with POODLE branching off to the side.

Ctrl + scroll to zoom
The timing staircase: each edge is the fix that was supposed to close the leak and the break that beat it, until authenticity-first ends it. POODLE is a parallel SSL 3.0 branch -- it has no channel to silence, so its only fix is retirement.

Channel one -- the loud error message (Vaudenay 2002, ASP.NET 2010). The first receivers simply returned a distinct "padding error," trivially observable. Vaudenay read it straight off the response, and eight years later Rizzo and Duong read it off differing HTTP responses from ASP.NET applications, recovering authentication tickets and web.config at scale [1] [2]. The obvious countermeasure: return one indistinguishable error for both padding failures and data failures. That closes the loudest channel and opens the next.

Channel two -- coarse timing (Canvel et al. 2003). Once OpenSSL returned the same bad_record_mac alert for padding and MAC failures, Brice Canvel, Alain Hiltgen, Serge Vaudenay, and Martin Vuagnoux showed the verdict still leaked through when the alert arrived: a receiver that accepted the padding went on to compute the MAC, while one that rejected padding could bail early, and the resulting millisecond gap reconstituted the oracle [13]. They intercepted a password over a live OpenSSL TLS channel. This is the first concrete demonstration of the pattern that organizes the whole subject. The standards response, TLS 1.1 (RFC 4346), mandated uniform alert handling and explicit per-record initialization vectors [14]. The countermeasure: make the decryption path constant-time -- compute the MAC even on padding failure so both paths take equal time.

Channel three -- statistical timing, Lucky Thirteen (AlFardan and Paterson, 2013). This is the break that proved constant-time is the hard problem, and it is worth binding tightly to the thesis, because it is not merely "a timing attack." TLS uses MAC-then-encrypt with a specific receiver order: decrypt, strip the padding, then run HMAC over what remains. The trouble is that "what remains" has a length that depends on how many bytes the receiver treated as padding -- and HMAC's running time depends on that length, because the hash compression function runs one extra time at certain length thresholds. So the attacker's guess about the padding changes the MAC-computation time by a few tens of CPU cycles [15].

Constant-time comparison

A comparison (or, more broadly, a code path) is constant-time when its running time does not depend on secret data or on how far into the data a difference occurs. A tag check that returns early on the first mismatched byte leaks where the mismatch is; a constant-time check XORs all bytes and inspects the accumulated difference once. Lucky Thirteen forced constant-time unpadding-and-MAC on the whole industry, and CVE-2016-2107 shows how easily a hand-written fast path reintroduces the oracle -- there, through a missing length check that produced a distinguishable alert rather than a timing gap.

Nadhem AlFardan and Kenneth Paterson amplified that sub-microsecond difference statistically across the network, recovering a full plaintext block in about 2232^{23} TLS sessions, dropping to roughly 2132^{13} sessions per byte with favorable positioning, across OpenSSL, GnuTLS, PolarSSL, and more (CVE-2013-0169) [15] [16]. Bind it to the ordering: MAC-then-encrypt forces a length-dependent MAC, so the timing leak is a property of the order of operations, not a stray implementation slip. The name "Lucky Thirteen" comes from the 13 bytes TLS feeds into the HMAC before the record body: a 5-byte TLS record header plus an 8-byte sequence number. Their alignment against the 64-byte hash-compression boundary is exactly what opens the timing gap. A number that sounds whimsical is really a byte-counting accident. The next countermeasure was obvious and genuinely hard: make the MAC and the unpadding constant-time.

Channel four -- unchecked structure, POODLE (Moller, Duong, and Kotowicz 2014). Here the staircase forks into a parallel branch. SSL 3.0's pad bytes are arbitrary by specification -- only the final length byte is constrained -- so there is no padding check to harden, no message to unify, no timing to flatten [11]. An attacker who maneuvers a target byte into the last position of a block learns it from MAC pass or fail alone, needing neither an error message nor a timing measurement, at an expected cost of 256 SSL 3.0 requests per byte [17] [18]. The name expands to Padding Oracle On Downgraded Legacy Encryption, and this is where a persistent misconception must be corrected. POODLE's downgrade is the enabler, not the bug. The downgrade only forces a modern client back onto vulnerable SSL 3.0; the vulnerability is the unchecked pad. The proof is TLS-POODLE (CVE-2014-8730), which hit TLS stacks that copied SSL 3.0's lax padding check and works with no downgrade at all [19]. Frame POODLE as a padding oracle first, a downgrade second. Google's write-up and the accompanying announcement documented the mechanism and effort in full [17] [20]. Because there is no check to make uniform, the only fix is to remove SSL 3.0, and CBC, from the wire entirely.

Channel five -- the oracle the fix re-created (CVE-2016-2107, 2016). This is the single clearest proof of the thesis. OpenSSL's AES-NI assembly fast path, written to make the Lucky Thirteen countermeasure constant-time, mishandled a length check -- and re-opened a padding oracle. It was found with the TLS-Attacker fuzzing methodology [21], and the National Vulnerability Database records the cause in words worth quoting exactly.

This vulnerability [CVE-2016-2107] exists because of an incorrect fix for CVE-2013-0169.

The constant-time patch for Lucky Thirteen re-created the very oracle it was written to close, and OpenSSL shipped the repair in 1.0.2h and 1.0.1t [22] [23]. It did not travel alone. Amazon's s2n library added a Lucky Thirteen countermeasure that still leaked, revived as Lucky Microseconds by Martin Albrecht and Kenneth Paterson -- and the randomized delays s2n had added as masking were part of the problem [24].

Then, in 2019, Craig Young's Zombie POODLE and GOLDENDOODLE and a large Internet scan by Robert Merget and colleagues found CBC padding oracles still live in 1.83% of the Alexa Top Million, distinguishable by the content of server responses with no precise timing required at all [25] [26].

Zombie POODLE is an invalid-padding, valid-MAC oracle; GOLDENDOODLE is the mirror image, a valid-padding, invalid-MAC oracle. Different verdicts, same atom.
IncidentYearLeak channelRoot causePrimary source
Vaudenay2002Distinct padding errorMtE-CBC observable branch[7]
Canvel et al.2003Coarse timingMAC computed vs skipped[13]
ASP.NET (Rizzo, Duong)2010Distinct HTTP errorPadding vs data error revealed[1] [2]
Lucky Thirteen2013Statistical timingStrip-then-MAC length dependence[15] [16]
POODLE2014MAC pass or fail on shifted byteSSL 3.0 unchecked padding[17] [18]
TLS-POODLE2014Same, no downgradeTLS stacks copying the lax check[19]
CVE-2016-21072016Distinguishable alert (bad_record_mac vs record_overflow)Constant-time fix slipped a length check[22]
Zombie POODLE, GOLDENDOODLE2019Content differences in responsesDeployed CBC-HMAC long tail, 1.83%[25] [26]

One clarification sharpens the definition by contrast. BEAST (2011) is often filed next to these breaks, but it is not a padding oracle: it is a predictable-initialization-vector, chosen-plaintext attack on CBC [27], and it belongs to the IV story of Part 2 and Part 5, not here. The tell is the direction of information flow. A padding oracle recovers unknown plaintext from the receiver's verdict on the attacker's ciphertext. BEAST confirms guessed plaintext by exploiting a predictable IV. Different precondition, different fix -- naming it here only sharpens what a padding oracle is.

Five generations, five channels, one bit. Every fix silenced the loudest remaining leak, and every fix was beaten by the next-quietest -- including a fix that became its own oracle. Whack-a-mole is not a strategy; it is a symptom. And the symptom points at a diagnosis the field had actually proved correct two years before Vaudenay's attack, and simply had not deployed.

5. The Bug Is the Order, Not the Padding

Stop looking at the padding. A padding oracle is not a padding bug; it is the consequence of checking padding on data you have not authenticated. Change what you check first, and the oracle has nothing to answer. To see why, you need the piece the attack has been quietly relying on: the MAC, and the three ways to combine it with encryption.

Message authentication code (MAC)

A MAC is a keyed tag that proves a message is authentic and unmodified: only a holder of the secret key can produce a tag that verifies. HMAC is the deployed instance. A valid MAC on a ciphertext means the ciphertext is genuine -- it came from someone with the key, and nothing tampered with it. Part 3 covers HMAC's structure; here the point is simply when you check it.

In 2000, Mihir Bellare and Chanathip Namprempre formalized the three "generic composition" ways to bolt a MAC onto an encryption scheme and proved exactly what each one buys [28]. Each maps cleanly onto a real protocol.

MAC-then-encrypt (MtE)

Compute the MAC over the plaintext, then encrypt the plaintext, MAC, and padding together. On receipt the order is forced: decrypt, strip padding, then verify the MAC. Because the receiver must decrypt and unpad before it can check authenticity, the padding verdict is observable by construction. This is SSL and TLS's original choice -- and the reason SSL/TLS, and only SSL/TLS, became a fifteen-year catalog of padding oracles.

Encrypt-then-MAC (EtM)

Encrypt the plaintext, then compute the MAC over the ciphertext (and the IV), and transmit ciphertext plus tag. On receipt, verify the tag first; decrypt only if it passes. A ciphertext the attacker altered fails the MAC and is discarded before any padding logic runs -- so there is no padding verdict to leak, through any channel. This is IPsec ESP's design and the shape RFC 7366 retrofits onto TLS.

The third sibling, encrypt-and-MAC, tags the plaintext and sends the tag alongside the ciphertext; SSH chose it. It still forces decryption before verification, and because the tag is over the plaintext it can leak plaintext equality, so it is no cure [29].

Ctrl + scroll to zoom
The three generic compositions, receiver-side. Only Encrypt-then-MAC verifies authenticity before anything parses the plaintext.

Look at where the MAC check sits. In the first two, unauthenticated ciphertext reaches the decrypt-and-parse machinery before anyone has proven it genuine. In the third, a forged ciphertext dies at the door.

CompositionMAC coversReceiver orderIND-CCA (generic)INT-CTXT (generic)Protocol
MAC-then-encryptPlaintextDecrypt, unpad, verifyNot guaranteedNoSSL and TLS
Encrypt-and-MACPlaintextDecrypt, verifyNoNoSSH
Encrypt-then-MACCiphertext and IVVerify, decrypt, unpadYesYesIPsec ESP

Now the theorem, stated precisely. Encrypt-then-MAC, composed from an IND-CPA encryption scheme and a strongly unforgeable MAC (SUF-CMA), generically attains both IND-CCA and integrity of ciphertexts -- the strongest guarantees of the three -- while MAC-then-encrypt and encrypt-and-MAC do not generically reach integrity of ciphertexts [28]. Strong unforgeability is the load-bearing word: with only ordinary unforgeability, a second valid tag on the same ciphertext is a fresh accepted ciphertext, and the guarantee weakens to integrity of plaintexts. In practice the qualifier is free, because HMAC is strongly unforgeable, so the deployed guarantee is exactly as stated. That is the whole game: a construction with integrity of ciphertexts rejects any forged ciphertext before decryption, so a padding oracle cannot form.

One honest caveat, because the sloppy version of this claim is wrong. Hugo Krawczyk proved in 2001 that MAC-then-encrypt is secure when the encryption is CBC or counter mode, under stated conditions [30]. So MtE is not "always broken." Krawczyk's result is conditional: MtE with CBC or CTR is provably secure in the model, which means Canvel, Lucky Thirteen, and POODLE are implementation, side-channel, and underspecified-padding failures -- not refutations of a theorem. The precise statement guards against the overreach "MtE is broken." What is true is subtler and more useful: MtE is conditionally provable and practically fragile, because it leaves unauthenticated ciphertext reaching the parser, and holding the model's assumptions in compiled code is the hard part [30]. The practitioner's takeaway is not "the theorem was wrong" but "the theorem's assumptions are unreasonably hard to preserve in real code -- so change the order and stop needing them." The practitioner literature has said as much for years [31].

Watch the difference on a single tampered ciphertext. The MtE receiver leaks a distinguishable outcome; the EtM receiver returns one indistinguishable failure before it parses anything.

JavaScript MAC-then-encrypt leaks a verdict; Encrypt-then-MAC does not
// Two toy receivers over the SAME tampered ciphertext.
// No real crypto: "decrypt" and "macOk" are stand-ins so you can see
// what each receiver reveals.
function decrypt(ct)  { return { paddingValid: false }; } // tampering broke the pad
function macOk(ct)    { return ct.tag === "authentic"; }  // forged tag fails

const tampered = { tag: "forged" };

// MAC-then-encrypt: decrypt and check padding BEFORE the MAC.
function mteReceive(ct) {
const p = decrypt(ct);
if (!p.paddingValid) return "PADDING_ERROR";  // leaks the padding verdict
if (!macOk(ct))      return "MAC_ERROR";
return "OK";
}

// Encrypt-then-MAC: verify the MAC FIRST; never touch padding on forgeries.
function etmReceive(ct) {
if (!macOk(ct)) return "FAIL";                // one indistinguishable outcome
const p = decrypt(ct);
if (!p.paddingValid) return "FAIL";
return "OK";
}

console.log("MtE says:", mteReceive(tampered)); // PADDING_ERROR (distinguishable)
console.log("EtM says:", etmReceive(tampered)); // FAIL (nothing to learn)

Press Run to execute.

The bug is the order, not the padding. Silencing channels is whack-a-mole; checking authenticity first -- Encrypt-then-MAC, and its successor AEAD -- removes the precondition that unauthenticated ciphertext ever reaches a padding check, so the oracle has nothing left to answer.

That is Aha number two, and it reorganizes the entire catalog. Every generation before Encrypt-then-MAC silenced a channel while leaving the precondition intact; the last two remove the precondition itself.

Ctrl + scroll to zoom
Five defensive generations. The first three silence a channel while the precondition survives; the last two remove the precondition by checking authenticity first.

AEAD (RFC 5116) is Encrypt-then-MAC's standardized successor: one primitive, authenticity intrinsic, no composition knob to misset [33]. The theorem was published in 2000. Vaudenay's attack landed in 2002. And the fix did not reach the deployed TLS base until 2014 to 2018. The rest of this article is what "check authenticity first" looks like when it finally shipped at Internet scale -- and why, even now, the class is not entirely dead.

6. The State of the Art: Closed by Construction

The modern answer is boring, and that is exactly the point. Use a primitive where a padding oracle cannot exist, because authenticity is checked first and there is no block padding at all.

AEAD (Authenticated Encryption with Associated Data)

An AEAD primitive takes a key, a nonce, a plaintext, and optional associated data, and produces a ciphertext with an integral authentication tag. Decryption verifies the tag as part of the operation and returns nothing -- a single indistinguishable failure -- if it does not match. There is no separate MAC to order, and for the stream-based AEADs in wide use there is no block padding to be oracular. Standardized as an interface in RFC 5116 [33].

Two AEADs carry nearly all modern traffic. AES-GCM is the default where AES hardware exists: it is AES in counter mode for encryption, plus a GHASH tag over the ciphertext and associated data, verified before any plaintext is released. ChaCha20-Poly1305 is the default where AES hardware does not exist -- Google deployed it in Chrome on Android because it runs about three times faster than AES-GCM on devices without AES acceleration, and it is constant-time in portable software by design [34] [35]. Both are counter-mode stream constructions, and that structural fact, not the label "AEAD," is why they are immune. A stream construction has no block padding at all -- the keystream is simply truncated to the plaintext length. So the object Vaudenay's atom acts on, a padded final block whose validity the receiver checks, does not exist. There is no surface. This is why "GCM has a tag, could it have a padding oracle?" answers itself: there is nothing to pad.

Then the standards removed the choice. TLS 1.3 (RFC 8446, 2018) removes CBC cipher suites entirely and mandates AEAD, and QUIC inherits that record layer, so a conformant modern stack cannot express the padding-oracle precondition [36].

For the deployed base that could not jump straight to 1.3, RFC 7366 (2014) retrofits Encrypt-then-MAC onto TLS 1.0 through 1.2 as a negotiated extension -- extension type 22 -- citing Bellare-Namprempre and Krawczyk by name as the reason MtE "is no longer regarded as secure" [32]. And BCP 195 (RFC 9325, 2022) codifies the operational rule: prefer TLS 1.3, prefer AEAD suites on 1.2, and recommend against CBC [37].

MethodHow it worksWhat it buysStatus 2024 to 2026
AES-GCMAES-CTR plus GHASH tag, verified firstNo padding surface, fastest with AES hardwareDefault (TLS 1.3, QUIC)
ChaCha20-Poly1305ChaCha20 plus Poly1305, verified firstConstant-time in software, ~3x without AES-NICo-default, WireGuard, OpenSSH
Encrypt-then-MAC (RFC 7366)MAC over IV and ciphertext, verified firstStructural fix for legacy CBCCompose-by-hand rule
Constant-time CBC-HMACHarden the MtE order in placeNothing new; fragileDeprecated (BCP 195)
Misuse-resistant, committing AEADAES-GCM-SIV, committing wrappersNonce-repeat safety, key commitmentAscending, niche
Padding-oracle detectionTLS-Attacker, padcheck scannersMeasures residual exposureActive tooling

The honest status has two levels. At the primitive level the class is closed: TLS 1.3 has no CBC-HMAC record layer, so it has no padding oracle. But CBC-HMAC survives in the long tail -- legacy TLS, VPN appliances, load balancers, IoT stacks, and home-grown application crypto -- which is why padding oracles are still discovered, in 1.83% of the top million as recently as the 2019 scan, exploitable without precise timing [25].

"Use AEAD" is the entire answer for greenfield code. But engineers inherit constraints -- a mode they cannot change, a library without AEAD, a protocol mid-migration -- so the real question is not "what is best" but "what are my options, ranked, and exactly when does each apply?"

7. How to Not Have a Padding Oracle, Ranked

There are three ways to keep a padding oracle out of your system, in strict order of durability -- and the ranking is a theorem, not a taste. The yardstick is one question: is authenticity checked before anything parses the plaintext?

First choice: AEAD. Authenticity is intrinsic, there is no padding, and there are zero composition knobs for an implementer to misset. This is the default, full stop. Its own frontier is a genuinely different problem, not a padding successor: nonce reuse under AES-GCM is catastrophic -- the "forbidden attack," demonstrated against real TLS and IPMI stacks by Böck et al. (WOOT '16) [38] -- and standard AEAD is not key-committing, so one ciphertext can be made to open to different valid plaintexts under different keys [39]. Those are addressed by AES-GCM-SIV for nonce-misuse resistance and by committing AEAD constructions, which the CFRG's property vocabulary now names formally [40] [41]. Treat that as a one-line signpost, not a reason to hesitate on AEAD.

Second choice: Encrypt-then-MAC by hand. Correct, but only if you get three things right -- an independent MAC key (never the encryption key), coverage of both the IV and the ciphertext, and a constant-time tag comparison performed before any decrypt or unpad. That is the RFC 7366 shape [32]. Three knobs an implementer can misset is precisely the argument for reaching for AEAD's zero knobs whenever you can.

Third choice, and only if forced: constant-time MtE / CBC-HMAC. This is the "make the existing order safe in place" path, and it is the cautionary option to retire, not to choose. Lucky Thirteen, Lucky Microseconds, and CVE-2016-2107 are the evidence that it fights the construction: MtE keeps unauthenticated ciphertext reaching the parser, so the distinguisher re-emerges through whatever the CPU or the compiler leaves open [15] [22].

PropertyAES-GCMChaCha20-Poly1305Encrypt-then-MAC (CBC-HMAC)Constant-time MtE (CBC-HMAC)AES-GCM-SIV
Authenticity checked first?Yes (intrinsic)Yes (intrinsic)Yes (verify tag first)No (decrypt and unpad first)Yes (intrinsic)
Padding-oracle surfaceNone (stream)None (stream)None (rejected pre-unpad)Full (precondition survives)None (stream)
Composition knobs to misset003Many0
Nonce or IV reuse behaviorCatastrophicCatastrophicIV must be unpredictableIV must be unpredictableGraceful (equality leak only)
Status 2024 to 2026Active defaultActive co-defaultCompose-by-hand ruleDeprecated (BCP 195)Ascending, niche

Only the first two options remove the precondition; the third merely silences the loudest channel. That is the whole ranking, and it maps onto the parallel history from the last section: AEAD and Encrypt-then-MAC are what IPsec effectively had from the start, while constant-time MtE is the fifteen-year attempt to make SSL/TLS's original order safe without changing it.

So the durable options all share one property: they refuse to let unauthenticated ciphertext reach a parser. That raises a deeper question a careful reader should now be asking. Why is one accept/reject bit enough to be fatal in the first place -- and is Encrypt-then-MAC provably enough to stop it, or just empirically better so far?

8. Why a Bit Is Enough, and Why the Fix Is a Theorem

Two boundaries frame the whole subject: how little leakage the attacker needs, which is frighteningly little, and how hard the defender's guarantee actually is, which is a proof rather than a hope.

The lower bound: one reliable bit suffices. A single accept/reject bit per query, combined with CBC's malleability, recovers plaintext at about 128 guesses per byte, and the cipher is never attacked [7]. The consequence is a hard limit on defense that every engineer should internalize: there is no "leak too small to matter," only "too noisy to measure cheaply." The bit is information-theoretically sufficient; the only variable is how many queries it takes to read it reliably.

The impossibility in the middle. An unauthenticated, malleable mode is provably not IND-CCA2. CBC without integrity is a decryption oracle by construction, so no amount of error-message hygiene or timing equalization can rescue MAC-then-encrypt CBC against a determined side-channel adversary. The impossibility lives in the precondition itself -- in letting a malleable ciphertext be decrypted before it is authenticated -- which is why the deployed padding oracle is exactly the IND-CCA2 break this series defines in Part 1.

The upper bound: the fix is a hard theorem. The best achievable defense is a proved one.

INT-CTXT (integrity of ciphertexts)

A scheme has integrity of ciphertexts when an adversary, even after seeing many valid ciphertexts, cannot produce any new ciphertext the receiver accepts as valid. This is the property that closes the oracle: if every forged ciphertext is rejected before decryption, the receiver never reveals a padding verdict on attacker-chosen input. Encrypt-then-MAC and AEAD provide it; MAC-then-encrypt does not, generically.

Encrypt-then-MAC with an IND-CPA cipher and a strongly unforgeable MAC (SUF-CMA) generically attains IND-CCA plus integrity of ciphertexts, so a forged ciphertext is rejected with overwhelming probability before decryption -- the oracle is closed by theorem, not by patch [28]. Krawczyk marks the honest boundary of the alternative: MAC-then-encrypt is conditionally provable for CBC and counter mode, so the real-world MtE breaks are implementation and side-channel failures, not refutations of a theorem -- but Encrypt-then-MAC needs no such conditions [30].

At the level of construction design there is no gap. The lower bound -- any observable padding verdict yields byte-at-a-time decryption -- is met by removing the precondition, and the upper bound -- reject forgeries before decryption, IND-CCA plus INT-CTXT -- is achieved by Encrypt-then-MAC and AEAD. Best-possible and best-achieved coincide. Every remaining gap is implementation and deployment, not mathematics.

That is Aha number three, and it is a humbler note than it first sounds. If the design problem is provably solved, why do scanners still find these oracles in production, and why did a correct construction get re-broken by its own patch? Because "solved on paper" and "solved in a compiled binary on a specific CPU, deployed to a billion devices" are different sentences -- and the space between them is where the class still lives.

9. Where Padding Oracles Still Live

The construction-level problem is closed; the class is not. Here are four places the 2002 bug remains exploitable, and one frontier that would end the whack-a-mole.

Constant-time is a moving target. Even with the correct construction, a specific compiled binary on a specific CPU can reintroduce a secret-dependent timing difference through branch prediction, cache behavior, or an assembly fast path -- and the countermeasure can also slip in a plainer way. CVE-2016-2107 is the canonical evidence: OpenSSL's AES-NI fast path, written to make the fix constant-time, dropped a length check and re-created the oracle -- leaking not through timing but through a distinguishable alert (bad_record_mac versus record_overflow), which is why TLS-Attacker found it by clustering responses rather than measuring cycles [22]. The strongest partial answer is formally verified, secret-independent cryptographic code -- HACL* and EverCrypt prove memory safety, functional correctness, and secret independence, and ship in Firefox and NSS [42]. But that covers primitives, not yet whole record layers end to end, so the verification frontier is real.

The deployed CBC-HMAC long tail. Legacy TLS, VPN appliances, load balancers, and IoT stacks that cannot move to TLS 1.3 keep MAC-then-encrypt CBC alive. The 2019 scan put the exposure at 1.83% of the top million, exploitable without precise timing, and the population shrinks year over year rather than vanishing -- a trend the SSL Pulse dashboard tracks [25] [43].

Application-layer oracles. This is the part TLS 1.3 does not reach, and it deserves its own aside because it is where the next decade of these bugs will be found.

Format and parse oracles beyond padding. Generalize once more: any observable post-decryption structural verdict -- a JSON parse success, a protobuf validity check, a decompression outcome -- is a padding oracle by another name whenever it acts on unauthenticated ciphertext. The padding was only ever the first structure anyone checked. Jager and Somorovsky already proved the mechanism outside padding entirely [44], and systematic detection of "decrypt-then-parse" leaks is not yet automated the way TLS padding-oracle scanning now is.

AEAD's own frontier. The successor primitive has two sharp edges of its own -- catastrophic nonce reuse in AES-GCM [38] and the lack of key commitment [39] -- which matter most in exactly the places large systems trip: nonce management across many distributed senders, and multi-key or multi-recipient contexts like JWT and envelope encryption. Both are addressed separately today, by AES-GCM-SIV for misuse resistance and by committing constructions that RFC 9771's vocabulary now specifies, but no single ubiquitous primitive yet delivers misuse-resistant and committing and fast and online at once [40] [41]. That convergence is the open engineering target, and it is a different problem from the padding oracle, not a continuation of it.

Every one of these is the same sentence wearing new clothes: unauthenticated, attacker-chosen input reaching a check whose verdict is observable. Which means the practical guide almost writes itself -- it is the thesis made operational.

10. Decision Rules: Use X With These Params in Case Y

Everything above collapses into a short decision procedure and a shorter list of nevers.

Ctrl + scroll to zoom
Choosing an authenticated-encryption construction in 2026. Reach for the leftmost option you can; only compose by hand when a mode is forced on you.

The rules, in order:

  • Default to a vetted AEAD, from a library you cannot misuse. Use AES-GCM with a unique 96-bit nonce and fewer than 2322^{32} messages per key where the CPU has AES-NI; use ChaCha20-Poly1305 where there is no AES hardware, or when you want constant-time behavior from portable software [34]. In application code, reach for libsodium's secretbox or Google Tink rather than raw primitives, and prefer any API where you cannot express "decrypt, then check" -- the safest libraries never hand you plaintext before verification.
  • If nonce uniqueness is hard to guarantee across many independent senders or under snapshot and rollback risk, use AES-GCM-SIV, so a nonce repeat degrades gracefully instead of catastrophically [40].
  • If a ciphertext may be opened under more than one key -- JWT and JOSE, envelope encryption, abuse reporting -- use a committing AEAD [39] [41].
  • If you must compose by hand, use Encrypt-then-MAC: encrypt, then compute the MAC over the IV and ciphertext with an independent MAC key, and verify the tag in constant time before you decrypt or unpad [32].
  • On a legacy TLS 1.2 CBC deployment you cannot yet retire, negotiate RFC 7366 Encrypt-then-MAC, prefer AEAD suites, and follow BCP 195 [37].
  • Never return a distinct "bad padding" error; never branch or vary timing on padding validity; never decrypt before verifying; never reuse the encryption key as the MAC key; never leave the IV out of the MAC; and never ship SSL 3.0 or non-RFC-7366 CBC.

If you are stuck at the fourth rule, the shape below is the reference. It is dependency-free and non-cryptographic on purpose: copy the order of operations, not the stand-in primitives.

JavaScript The correct Encrypt-then-MAC open(): verify first, in constant time
// Reference SHAPE for a correct Encrypt-then-MAC open().
// constantTimeEqual, hmac, cbcDecrypt, removePkcs7 are stand-ins, NOT real crypto.
function constantTimeEqual(a, b) {
if (a.length !== b.length) return false;
let diff = 0;
for (let i = 0; i < a.length; i++) diff |= a[i] ^ b[i];
return diff === 0;   // no early exit: time is independent of where they differ
}

function open(kEnc, kMac, iv, ciphertext, tag) {
// 1. Authenticate FIRST, over IV concat ciphertext, in constant time.
const expected = hmac(kMac, iv.concat(ciphertext));
if (!constantTimeEqual(tag, expected)) return "FAIL";  // one outcome, always

// 2. Only authentic ciphertext ever reaches decryption and unpadding.
const padded = cbcDecrypt(kEnc, iv, ciphertext);
const plain = removePkcs7(padded);
return plain === null ? "FAIL" : plain;                // same FAIL either way
}

// Stand-ins so the snippet runs (NOT secure):
function hmac(k, data)          { return data.map((b) => (b ^ k) & 0xff); }
function cbcDecrypt(k, iv, c)   { return c; }
function removePkcs7(p)         { return p; }

console.log(open(1, 2, [9, 9], [4, 5, 6], [7, 7, 7])); // FAIL: forged tag rejected first

Press Run to execute.

Every misuse in real code maps one-to-one onto a named break in the catalog. That mapping is the practical guide as a grid.

Misuse seen in codeThe named break it reproducesThe fix
Distinct "bad padding" errorVaudenay, ASP.NETVerify authenticity first
Non-constant-time unpad and MACLucky ThirteenAEAD, or constant-time EtM
Unchecked pad bytesPOODLERetire SSL 3.0 and CBC
AES-NI fast-path length slipCVE-2016-2107Verified constant-time, or AEAD
Verifying the MAC after decryptingThe whole classEncrypt-then-MAC ordering
Nonce reuse under AES-GCMThe forbidden attackUnique nonces, or AES-GCM-SIV
App-layer encrypt with no MACJWE and cookie oraclesA committing AEAD

Detection is cheap enough to wire into a pipeline, and you should, because content-difference oracles need no timing rig to find [25].

How to check your own endpoints for a CBC padding oracle

Point an open-source padding-oracle scanner at your own hosts. Craig Young's padcheck and the TLS-Attacker project's TLS-Padding-Oracles both probe for the invalid-padding-versus-invalid-MAC content differences that Zombie POODLE and GOLDENDOODLE exploit, and they run without a precise timing setup [46] [47]. A clean result on a CBC endpoint is reassuring; any distinguishable response is a reportable finding, not a tuning opportunity.

The checklist is short because the lesson is one sentence. Before restating it, clear the confident, wrong sentences that keep this bug alive in design meetings.

11. Misconceptions, Named and Corrected

The padding oracle survives mostly because a handful of confident, wrong sentences keep getting said in design meetings. Here they are, corrected.

Padding oracles, misconceptions corrected

It is AES-256 -- isn't that unbreakable?

The strength of AES is irrelevant to this attack, because the key is never attacked. A padding oracle recovers plaintext from the receiver's error handling -- a distinguishable answer to "was the padding valid?" -- combined with CBC's malleability [7]. The ASP.NET break recovered authentication tickets and web.config with AES-256 fully intact [1]. "The cipher is strong" and "the system is secure" are different claims.

We hid the padding-error message -- aren't we safe now?

No. The same accept/reject bit re-emerges through the next-quietest channel. Once the error message was unified, it leaked through coarse timing (Canvel et al., 2003) and then through statistical timing under unified errors (Lucky Thirteen, 2013), and through unchecked structure entirely (POODLE, 2014) [13] [15] [17]. Silencing one channel just moves the leak; only removing the precondition closes it.

So MAC-then-encrypt is broken?

No -- and the overstatement matters. Krawczyk proved MAC-then-encrypt conditionally secure for CBC and counter mode, so the real-world breaks are implementation, side-channel, and underspecified-padding failures, not refutations of a theorem [30]. What is true is that MtE is fragile in practice: it leaves unauthenticated ciphertext reaching the parser, so constant-time MtE is a fight against the construction. The fix is to change the order, not to declare a theorem false.

Isn't POODLE a downgrade attack?

It is a padding oracle; the downgrade is only the enabler that forces a client onto vulnerable SSL 3.0, whose pad bytes are unchecked by specification [17] [11]. The proof is TLS-POODLE (CVE-2014-8730), the same oracle against TLS stacks that copied the lax check, with no downgrade at all [19]. Frame POODLE as a padding oracle first, a downgrade second.

Wasn't Bleichenbacher the first CBC padding oracle?

No. Bleichenbacher (1998) is an RSA PKCS #1 v1.5 oracle -- a different primitive with different arithmetic [3]. Vaudenay (2002) is the first CBC instance [7]. Bleichenbacher is the ancestor "in spirit," because he first showed that a structural validity verdict is a decryption oracle, but calling his attack a CBC padding oracle confuses two branches of the family tree.

GCM has a tag and a nonce -- can it have a padding oracle?

No. AES-GCM and ChaCha20-Poly1305 are counter-mode stream constructions with no block padding, and they verify the authentication tag before releasing any plaintext, so the Vaudenay atom has no surface to act on [34]. GCM's own hazard is nonce reuse, which is a different problem -- the "forbidden attack" -- not a padding oracle [38].

Is a constant-time MAC compare enough to be safe?

Only if authenticity is checked first and the unpadding is also constant-time. A constant-time compare on a MAC you verify after decrypting still leaves the padding check reachable, which is the whole class -- and a hand-written fast path can reintroduce the oracle anyway, as CVE-2016-2107 did, through a missing length check observable as a distinguishable alert rather than a timing leak [22]. The safe pattern is verify-then-decrypt, or an AEAD that gives you no other option.

Every correction points at the same root: the padding was never the bug. Time to say the sentence the whole article was built to earn.

Decrypt, Then Confess

Return to that ASP.NET server in September 2010. AES-256 intact. The key never touched, never even approached. And the plaintext -- authentication tickets, web.config, the site's master secrets -- gone, one byte at a time, because the server answered a question it should never have been asked before it authenticated the ciphertext [1].

Line up the catalog and the shape is unmistakable. Bleichenbacher, Vaudenay, Canvel, ASP.NET, Lucky Thirteen, POODLE, CVE-2016-2107, Zombie POODLE: in every case the cipher did exactly what it promised, and the variable was always the order of operations, leaking through a quieter channel each time it was silenced. A loud error became coarse timing became statistical timing became unchecked structure became a fix that re-created its own oracle. Five generations chased the bit; the bit kept escaping, because the precondition -- unauthenticated, malleable ciphertext reaching a padding check -- was never removed.

The resolution the field finally deployed is a single move: verify authenticity first. Encrypt-then-MAC proves it -- IND-CCA plus integrity of ciphertexts, the strongest of the three generic compositions, published two years before Vaudenay's attack [28]. AEAD ships it as one primitive with no knob to misset, and TLS 1.3 makes it the only option a conformant stack can express [36].

This mirrors the running lesson of the series: Part 1's IND-CCA2 adversary is not a chalkboard abstraction but the exact adversary that walked out of that ASP.NET server, and Part 5's warning that confidentiality is not integrity is precisely the gap a padding oracle drives through.

When a ciphertext you did not create arrives, what does your receiver reveal about it before it has proven the ciphertext authentic? Encrypt-then-MAC and AEAD are the discipline of making the answer nothing.

Carry that one question into every protocol you design or review. Drop Lucky Thirteen onto it, drop POODLE onto it, drop the next application-layer JWE oracle onto it: each is a non-empty answer, and the fix is always the same shape. The cipher was never the weak link, and no future cipher will be. That is why this is Part 6 of a field guide to protocol design, not a chapter on block ciphers.

Study guide

Key terms

Padding oracle
An attack class where a receiver's observable verdict on the padding of attacker-chosen ciphertext becomes a plaintext-recovery oracle, never touching the key.
PKCS#7 padding
Append n bytes each equal to n to fill the final block; a full extra block when already aligned (RFC 2315).
CBC malleability
Because each plaintext block is D_k(C_i) XOR C_(i-1), flipping a ciphertext byte flips the same plaintext byte deterministically.
Chosen-ciphertext attack (IND-CCA2)
An adversary that submits ciphertexts and learns from the receiver's reactions; a padding oracle is a deployed instance.
Message authentication code (MAC)
A keyed tag proving a message is authentic and unmodified; HMAC is the deployed instance.
MAC-then-encrypt (MtE)
MAC the plaintext, then encrypt; the receiver decrypts and unpads before verifying, so the padding verdict is observable by construction. SSL and TLS's original choice.
Encrypt-then-MAC (EtM)
MAC the ciphertext and IV, verify first, decrypt only authentic ciphertext; attains IND-CCA plus INT-CTXT. IPsec ESP and RFC 7366.
INT-CTXT (integrity of ciphertexts)
The adversary cannot produce any new ciphertext the receiver accepts; the property that closes the oracle.
AEAD
Authenticated Encryption with Associated Data: one primitive that verifies authenticity as part of decryption and returns nothing on failure (RFC 5116).
Constant-time comparison
A check whose running time does not depend on secret data or on where a difference occurs; the countermeasure Lucky Thirteen forced, whose hand-written implementation CVE-2016-2107 later showed to be fragile.

References

  1. Daniel Bleichenbacher (1998). Chosen Ciphertext Attacks Against Protocols Based on the RSA Encryption Standard PKCS #1. https://doi.org/10.1007/BFb0055716
  2. Serge Vaudenay (2002). Security Flaws Induced by CBC Padding -- Applications to SSL, IPSEC, WTLS. https://doi.org/10.1007/3-540-46035-7_35
  3. B. Kaliski (1998). PKCS #7: Cryptographic Message Syntax Version 1.5. https://www.rfc-editor.org/rfc/rfc2315
  4. T. Dierks & E. Rescorla (2008). The Transport Layer Security (TLS) Protocol Version 1.2. https://www.rfc-editor.org/rfc/rfc5246
  5. A. Freier, P. Karlton, & P. Kocher (2011). The Secure Sockets Layer (SSL) Protocol Version 3.0. https://www.rfc-editor.org/rfc/rfc6101
  6. K. Moriarty, B. Kaliski, & A. Rusch (2017). PKCS #5: Password-Based Cryptography Specification Version 2.1. https://www.rfc-editor.org/rfc/rfc8018
  7. Brice Canvel, Alain Hiltgen, Serge Vaudenay, & Martin Vuagnoux (2003). Password Interception in a SSL/TLS Channel. https://doi.org/10.1007/978-3-540-45146-4_34
  8. T. Dierks & E. Rescorla (2006). The Transport Layer Security (TLS) Protocol Version 1.1. https://www.rfc-editor.org/rfc/rfc4346
  9. Robert Merget, Juraj Somorovsky, Nimrod Aviram, Craig Young, Janis Fliegenschmidt, Jörg Schwenk, & Yuval Shavitt (2019). Scalable Scanning and Automatic Classification of TLS Padding Oracle Vulnerabilities. https://www.usenix.org/conference/usenixsecurity19/presentation/merget
  10. Hugo Krawczyk (2001). The Order of Encryption and Authentication for Protecting Communications (or: How Secure Is SSL?). https://doi.org/10.1007/3-540-44647-8_19
  11. P. Gutmann (2014). Encrypt-then-MAC for Transport Layer Security (TLS) and Datagram Transport Layer Security (DTLS). https://www.rfc-editor.org/rfc/rfc7366
  12. D. McGrew (2008). An Interface and Algorithms for Authenticated Encryption. https://www.rfc-editor.org/rfc/rfc5116
  13. Y. Nir & A. Langley (2018). ChaCha20 and Poly1305 for IETF Protocols. https://www.rfc-editor.org/rfc/rfc8439
  14. E. Rescorla (2018). The Transport Layer Security (TLS) Protocol Version 1.3. https://www.rfc-editor.org/rfc/rfc8446
  15. Y. Sheffer, P. Saint-Andre, & T. Fossati (2022). Recommendations for Secure Use of Transport Layer Security (TLS) and DTLS (BCP 195). https://www.rfc-editor.org/rfc/rfc9325
  16. S. Gueron, A. Langley, & Y. Lindell (2019). AES-GCM-SIV: Nonce Misuse-Resistant Authenticated Encryption. https://www.rfc-editor.org/rfc/rfc8452.html
  17. J. Len, P. Grubbs, & T. Ristenpart (2025). Properties of Authenticated Encryption with Associated Data (AEAD). https://www.rfc-editor.org/rfc/rfc9771.html
  18. Damian Poddebniak, Christian Dresen, Jens Müller, Fabian Ising, Sebastian Schinzel, Simon Friedberger, Juraj Somorovsky, & Jörg Schwenk (2018). Efail: Breaking S/MIME and OpenPGP Email Encryption using Exfiltration Channels. https://efail.de/ - Poddebniak et al., USENIX Security 2018; CBC gadget targets S/MIME, CFB gadget targets OpenPGP.
  19. Craig Young (2019). padcheck: a CBC padding-oracle scanner. https://github.com/tripwire/padcheck
  20. Juliano Rizzo & Thai Duong (2010). Practical Padding Oracle Attacks. https://www.usenix.org/events/woot10/tech/full_papers/Rizzo.pdf
  21. (2010). CVE-2010-3332: ASP.NET Padding Oracle Vulnerability. https://nvd.nist.gov/vuln/detail/CVE-2010-3332
  22. Hanno Böck, Juraj Somorovsky, & Craig Young (2018). ROBOT: Return Of Bleichenbacher's Oracle Threat. https://robotattack.org/
  23. Hubert Kario (2023). The Marvin Attack. https://people.redhat.com/~hkario/marvin/
  24. Nadhem J. AlFardan & Kenneth G. Paterson (2013). Lucky Thirteen: Breaking the TLS and DTLS Record Protocols. https://www.isg.rhul.ac.uk/tls/Lucky13.html
  25. (2013). CVE-2013-0169: the Lucky Thirteen issue. https://nvd.nist.gov/vuln/detail/CVE-2013-0169
  26. Bodo Möller, Thai Duong, & Krzysztof Kotowicz (2014). This POODLE Bites: Exploiting The SSL 3.0 Fallback. https://www.openssl.org/~bodo/ssl-poodle.pdf
  27. Bodo Möller (2014). This POODLE Bites: Exploiting the SSL 3.0 Fallback. https://security.googleblog.com/2014/10/this-poodle-bites-exploiting-ssl-30.html
  28. (2014). CVE-2014-3566: the POODLE issue. https://nvd.nist.gov/vuln/detail/CVE-2014-3566
  29. (2014). CVE-2014-8730: TLS-POODLE (F5 BIG-IP CBC padding check). https://nvd.nist.gov/vuln/detail/CVE-2014-8730
  30. (2016). CVE-2016-2107: padding oracle in AES-NI CBC MAC check. https://nvd.nist.gov/vuln/detail/CVE-2016-2107
  31. OpenSSL Project (2016). OpenSSL Security Advisory: Padding oracle in AES-NI CBC MAC check (CVE-2016-2107). https://www.openssl.org/news/secadv/20160503.txt
  32. Juraj Somorovsky (2016). Systematic Fuzzing and Testing of TLS Libraries. https://eprint.iacr.org/2016/425
  33. Martin R. Albrecht & Kenneth G. Paterson (2016). Lucky Microseconds: A Timing Attack on Amazon's s2n Implementation of TLS. https://eprint.iacr.org/2015/1129
  34. Craig Young (2019). Zombie POODLE and GOLDENDOODLE Vulnerabilities. https://blog.qualys.com/product-tech/2019/04/22/zombie-poodle-and-goldendoodle-vulnerabilities
  35. Mihir Bellare & Chanathip Namprempre (2000). Authenticated Encryption: Relations among Notions and Analysis of the Generic Composition. https://link.springer.com/chapter/10.1007/3-540-44448-3_41
  36. Mihir Bellare, Tadayoshi Kohno, & Chanathip Namprempre (2002). Authenticated Encryption in SSH: Provably Fixing the SSH Binary Packet Protocol. https://doi.org/10.1145/586110.586112
  37. Elie Bursztein (2014). Speeding up and strengthening HTTPS connections for Chrome on Android. https://security.googleblog.com/2014/04/speeding-up-and-strengthening-https.html
  38. Hanno Böck, Aaron Zauner, Sean Devlin, Juraj Somorovsky, & Philipp Jovanovic (2016). Nonce-Disrespecting Adversaries: Practical Forgery Attacks on GCM in TLS. https://www.usenix.org/conference/woot16/workshop-program/presentation/bock
  39. Ange Albertini, Thai Duong, Shay Gueron, Stefan Kölbl, Atul Luykx, & Sophie Schmieg (2022). How to Abuse and Fix Authenticated Encryption Without Key Commitment. https://www.usenix.org/conference/usenixsecurity22/presentation/albertini
  40. Project Everest (2024). HACL*: A formally verified cryptographic library. https://github.com/hacl-star/hacl-star
  41. Tibor Jager & Juraj Somorovsky (2011). How to Break XML Encryption. https://doi.org/10.1145/2046707.2046756
  42. Qualys SSL Labs (2024). SSL Pulse. https://www.ssllabs.com/ssl-pulse/
  43. TLS-Attacker (2019). TLS-Padding-Oracles research repository. https://github.com/tls-attacker/TLS-Padding-Oracles
  44. Jonathan Katz & Yehuda Lindell (2020). Introduction to Modern Cryptography, 3rd edition. ISBN 9780815354369.
  45. Niels Ferguson, Bruce Schneier, & Tadayoshi Kohno (2010). Cryptography Engineering: Design Principles and Practical Applications. ISBN 9780470474242.
  46. Nimrod Aviram, Sebastian Schinzel, & Juraj Somorovsky (2016). DROWN: Breaking TLS Using SSLv2. https://drownattack.com/ - Official DROWN site; Aviram et al., USENIX Security 2016.
  47. Thai Duong & Juliano Rizzo (2011). Here Come The XOR Ninjas. https://web.archive.org/web/20120103081525/http://netifera.com/research/beast/beast_DRAFT_0621.pdf - The BEAST attack: predictable-IV chosen-plaintext attack on TLS 1.0 CBC.