We don't want Oracles
"Oracle" is defined as a system that leaks information to unauthorized users. This is a cyber and software engineering specific term that you should be aware of as its often a more nuanced thing and often forgotten in threat modeling.
#1 Error Messages
The most common leaked secret is whether an account exists. The login form is usually the worst offender: returning "No account found" vs. "Incorrect password" gives an attacker a validated target list for password spraying. "This account exists" leaks everywhere: registration ("Email taken"), password resets, and magic links. Stack traces on a 500 error act as oracles for the entire infrastructure.
The fix: Review error messages in E2E testing and be ready to take a hit on the UX front in favor of security.
#2 Timing
A 2ms response means no user; a 180ms response means a real user with a wrong password. Anywhere timing can be analyzed it will cause timing oracles.
The fix: Benchmark timing across execution branches and make them equal cost. At the primitive level this is fixed by implementing constant-time paradigms.
#3 Crypto Padding
This is the attack the word "oracle" was coined for. Block ciphers in CBC mode require fixed-size blocks. The leftover space gets padded, usually with PKCS#7, where the byte value matches the number of padded bytes (three bytes of padding = 03 03 03). When decrypting, the server checks this padding first.
The oracle is simple: the server tells you if the padding was valid. It leaks this through a specific error message, a different HTTP status code, or just taking slightly longer to respond.
An attacker manipulates the ciphertext one byte at a time and sends it back. By watching the server's response to see when the padding check passes, they figure out the underlying plaintext byte by byte. They don't need the key. A few thousand requests per block and your encrypted payload is completely exposed. This is how POODLE worked. Throwing a generic 500 error for bad padding doesn't fix a damn thing if the attacker can still tell the difference.
The fix: Don't roll your own crypto.
#4 Response Shape
Response shape applies to anything in your application response.
Status codes: Returning a 403 instead of a 404 tells an attacker "this exists, you just can't see it." That is an oracle for enumerating private object IDs, internal usernames, or feature flags.
Response size: If two generic error pages differ by a few hundred bytes, the attacker knows which backend path executed. They don't need to read the content; they just measure the length. This is the foundation of blind injection attacks.
Redirects: A 302 redirect exposes state if it sends a logged-in user to a different destination than a logged-out user.
Headers and cookies: A Set-Cookie header that only drops on a success path, an ETag tied to a specific user, or a cache directive that changes based on privilege level.
Application behavior: Rate limiters that only trigger for valid accounts, or API endpoints that take longer to respond when a database record actually exists. It's a timing oracle moved up the stack.
The fix: Don't do those things.
The Unifying Principle of Oracle Attacks
is anything an attacker can observe. The defense is indistinguishably (which rolls off the tongue so beautifully it makes me happy I am not a podcast host).
Threat modelers should ask:
1. Does the response change based on anything the user shouldn't know?
2. Where and what does it leak?
3. Can they query it enough times to turn that bit of information into the whole answer?