CWE-203, Observable Discrepancy, describes a product that behaves differently, or sends different responses, under different circumstances, in a way an unauthorized actor can observe. Nothing has to be returned directly. The difference itself is the leak.
It hits almost anything that makes a security decision: login forms, token checks, file handlers, cryptographic code, even hardware. If the answer to "was that guess right?" can be read from the outside, an attacker can use it.
How it happens
A system makes a decision, and the decision leaves a trace. MITRE lists several kinds of trace in the examples attached to this weakness.
- Timing. A comparison that stops at the first wrong byte takes longer when more bytes are right. CVE-2019-10071 is described as a Java-oriented framework comparing HMAC signatures with String.equals() instead of a constant-time algorithm, which caused timing discrepancies.
- Error responses. CVE-2002-2094 and related entries use ".." path attacks and watch the error responses to learn what exists.
- Power and hardware behavior. CVE-2019-14353 describes a crypto hardware wallet whose power consumption relates to the number of pixels lit, creating a side channel in the USB connection that exposes secrets shown on screen, such as a PIN. CVE-2020-8695 covers an observable discrepancy in the RAPL interface of some Intel processors.
CWE-203 is a parent category. Timing, response and behavioral discrepancies are covered by more specific weaknesses, including CWE-204, CWE-205 and CWE-208. If you can name the specific channel, use the specific entry.
A vulnerable pattern
# Vulnerable: == returns as soon as a byte differs
def verify(token: str, expected: str) -> bool:
return token == expected
The comparison is fast when the first character is wrong and slower when more leading characters match. Over many requests, that difference can be measured.
What an attacker gains
MITRE records two consequences. First, an attacker can learn sensitive information about the system, including authentication information that may help them get in, and can bypass protection mechanisms. Second, in some cases the discrepancies form a side channel. If the code involved is cryptographic and vulnerable to side-channel attacks, the worst case is revealing unencrypted plaintext.
Notice that the attacker never breaks the lock. They learn how the lock behaves and work from there.
How to find it
MITRE does not list detection methods for this entry, so rely on review and testing.
- Find every place your code compares a secret: tokens, signatures, password hashes, API keys, MACs. Check whether the comparison is constant time.
- For each login, reset and lookup flow, send a valid and an invalid input and compare the full response: status, body, headers and latency.
- Review error handling on file and path operations. Different errors for "missing" and "forbidden" tell an attacker what exists.
- Check dependencies. A comparison bug can live in a library you did not write, and the CVE record is how you learn about it. A free in-browser dependency scan runs without login, keeps the file on your machine and covers up to 100 dependencies.
How to fix it
MITRE's mitigations for this weakness cover design and implementation.
- Compartmentalize. Draw clear trust boundaries and keep sensitive data inside them. Apply least privilege.
- Keep error messages minimal. Give the intended audience only what they need. Do not reveal how the error was determined. If you must record detail, put it in logs, and never write highly sensitive information such as passwords to them. Avoid inconsistent messaging that tips off an attacker about internal state, such as whether an account exists.
- Compare secrets in constant time.
import hmac
def verify(token: str, expected: str) -> bool:
return hmac.compare_digest(token.encode(), expected.encode())
In Java, MessageDigest.isEqual serves the same purpose for byte arrays.
Quick check
Why is a fast "wrong" response a problem?
If the time or content of a response depends on how close a guess was, the attacker can use that signal to improve each next guess.
Is a generic error message enough?
Not on its own. Status codes, headers, response size and latency can also differ. Compare the whole response, not just the text.