Observable Response Discrepancy

Description

Observable Response Discrepancy is a vulnerability that occurs when a product provides different responses to incoming requests based on internal state, revealing information to unauthorized actors. Unlike timing-based discrepancies, this weakness specifically involves variations in the content, structure, or type of responses returned by the system. Common manifestations include different error messages for valid versus invalid usernames, distinct HTTP status codes based on authentication state, or varying response content that reveals whether specific resources exist. Attackers analyze these response differences to enumerate valid accounts, discover hidden functionality, fingerprint systems, or gather intelligence for further attacks.

Risk

Observable response discrepancies create significant security risks by enabling attackers to gather sensitive information through careful analysis of system responses. Username enumeration allows attackers to compile lists of valid accounts for targeted password attacks, credential stuffing, or social engineering campaigns. System fingerprinting through response analysis helps attackers identify software versions, configurations, and potential vulnerabilities to exploit. In authentication systems, response differences can reveal password policies, account lockout states, or multi-factor authentication status. These information leaks transform blind attacks into targeted ones, significantly increasing the likelihood of successful compromise while reducing the time and resources attackers need to invest.

Solution

Implement consistent, generic error messages across all authentication and authorization failure scenarios. Use identical response messages such as "Authentication failed" or "Invalid credentials" regardless of whether the username, password, or both are incorrect. Ensure HTTP status codes remain consistent for different failure types - avoid returning 401 for valid users and 404 for invalid ones. Implement response normalization at the application boundary to strip internal state indicators before returning data to clients. Configure web servers and frameworks to return uniform error pages. For sensitive operations, consider implementing fixed response times alongside consistent messaging. Regularly audit application responses using automated tools to detect unintentional information leakage in error handling paths.

Common Consequences

ImpactDetails
Confidentiality, Access ControlScope: Confidentiality, Access Control

Attackers can analyze response differences to read sensitive application data and bypass protection mechanisms. Username enumeration enables targeted attacks, while system fingerprinting reveals configuration details that aid in identifying exploitable vulnerabilities.
ConfidentialityScope: Confidentiality

Response discrepancies can expose the existence of protected resources, valid credentials, internal system states, and configuration details. This information leakage compromises data confidentiality even when direct access to the data remains protected.

Example Code

Vulnerable Code (Java)

The following code demonstrates a vulnerable login endpoint where different error messages reveal whether a username exists:

@RestController
public class VulnerableAuthController {

    @Autowired
    private UserRepository userRepository;

    @Autowired
    private PasswordEncoder passwordEncoder;

    @PostMapping("/api/login")
    public ResponseEntity<?> login(@RequestBody LoginRequest request) {

        // Vulnerable: Different responses reveal username validity
        User user = userRepository.findByUsername(request.getUsername());

        if (user == null) {
            // Response A: Reveals username doesn't exist
            return ResponseEntity.status(HttpStatus.NOT_FOUND)
                .body(new ErrorResponse("User not found"));
        }

        if (!passwordEncoder.matches(request.getPassword(), user.getPasswordHash())) {
            // Response B: Reveals username exists but password wrong
            return ResponseEntity.status(HttpStatus.UNAUTHORIZED)
                .body(new ErrorResponse("Incorrect password"));
        }

        // Check if account is locked
        if (user.isLocked()) {
            // Response C: Reveals account exists and is locked
            return ResponseEntity.status(HttpStatus.FORBIDDEN)
                .body(new ErrorResponse("Account is locked"));
        }

        String token = generateToken(user);
        return ResponseEntity.ok(new AuthResponse(token));
    }
}

The vulnerability exists because each failure case returns a distinct HTTP status code and error message, allowing attackers to determine: (1) whether a username exists, (2) whether the password is wrong, and (3) whether the account is locked.

Fixed Code (Java)

@RestController
public class SecureAuthController {

    @Autowired
    private UserRepository userRepository;

    @Autowired
    private PasswordEncoder passwordEncoder;

    // Generic error message used for all authentication failures
    private static final String AUTH_FAILURE_MESSAGE = "Invalid username or password";
    private static final HttpStatus AUTH_FAILURE_STATUS = HttpStatus.UNAUTHORIZED;

    @PostMapping("/api/login")
    public ResponseEntity<?> login(@RequestBody LoginRequest request) {

        // Always perform the same operations regardless of user existence
        User user = userRepository.findByUsername(request.getUsername());

        // Create dummy user for timing consistency when user doesn't exist
        String passwordToCheck = (user != null) ? user.getPasswordHash() : getDummyHash();

        // Always perform password check to maintain consistent timing
        boolean passwordValid = passwordEncoder.matches(
            request.getPassword(),
            passwordToCheck
        );

        // Check all conditions but return identical error
        if (user == null || !passwordValid || user.isLocked()) {
            // Log the specific reason internally for security monitoring
            logAuthFailure(request.getUsername(), user, passwordValid);

            // Return generic response - same message, same status for all failures
            return ResponseEntity.status(AUTH_FAILURE_STATUS)
                .body(new ErrorResponse(AUTH_FAILURE_MESSAGE));
        }

        String token = generateToken(user);
        return ResponseEntity.ok(new AuthResponse(token));
    }

    private String getDummyHash() {
        // Pre-computed hash to ensure consistent timing
        return "$2a$10$dummyhashfordummyuserxxxxxxxxxxxxxxxxxxxxxxxx";
    }

    private void logAuthFailure(String username, User user, boolean passwordValid) {
        // Internal logging for security team - never exposed to client
        if (user == null) {
            securityLogger.warn("Login attempt for non-existent user: {}", username);
        } else if (!passwordValid) {
            securityLogger.warn("Invalid password for user: {}", username);
        } else if (user.isLocked()) {
            securityLogger.warn("Login attempt on locked account: {}", username);
        }
    }
}

The fix returns identical HTTP status codes and error messages for all authentication failures while maintaining detailed internal logging for security monitoring. A dummy password hash ensures consistent processing time even for non-existent users.


Exploited in the Wild

Yahoo Email Enumeration (Yahoo, 2013-2014)

Attackers exploited Yahoo's password reset functionality which returned different responses for valid and invalid email addresses. By automating requests to the password reset endpoint and analyzing the varying responses, attackers compiled massive lists of valid Yahoo email addresses. These lists were subsequently used in credential stuffing attacks and phishing campaigns, contributing to the breach that affected over 3 billion Yahoo accounts.

LinkedIn User Enumeration for Data Scraping (LinkedIn, 2021)

Attackers exploited LinkedIn's API endpoints which exhibited response discrepancies that revealed whether user IDs were valid. By systematically querying the API and analyzing different response patterns, attackers scraped profile data from over 700 million LinkedIn users. The compiled data was subsequently sold on dark web forums, exposing personal and professional information of LinkedIn's user base.

Keycloak Client Enumeration (Multiple Organizations, 2024)

A vulnerability in Keycloak, the popular open-source identity and access management solution, allowed attackers to enumerate registered clients on realms through observable response discrepancies. The server returned "Invalid Request" for existing clients but "Client not found" for non-existing ones. This information disclosure affected organizations using Keycloak for authentication across their applications.


Tools to Test/Exploit

  • Burp Suite Intruder — Automated tool for sending parameterized requests and analyzing response differences to detect enumeration vulnerabilities.

  • OWASP ZAP — Open-source web application security scanner that can detect response discrepancies through active scanning and fuzzing techniques.

  • Wfuzz — Web application fuzzer that can identify response discrepancies by comparing HTTP responses across different inputs.


CVE Examples

  • CVE-2025-24023 — Flask-AppBuilder user enumeration vulnerability where timing differences in database authentication reveal valid usernames.

  • CVE-2024-45052 — Fides Webserver timing-based username enumeration allows unauthenticated attackers to determine valid usernames.

  • CVE-2001-1483 — Application returns inconsistent responses allowing attackers to enumerate valid usernames.

  • CVE-2004-0243 — Operating system reveals password correctness through different error messages before fully authenticating.

  • CVE-2004-1428 — FTP server prompts for password only when username exists, enabling user enumeration.


References

  1. MITRE Corporation. "CWE-204: Observable Response Discrepancy." Common Weakness Enumeration. https://cwe.mitre.org/data/definitions/204.html

  2. OWASP Foundation. "Testing for Account Enumeration and Guessable User Account." OWASP Web Security Testing Guide. https://owasp.org/www-project-web-security-testing-guide/latest/4-Web_Application_Security_Testing/03-Identity_Management_Testing/04-Testing_for_Account_Enumeration_and_Guessable_User_Account

  3. Pentest-Tools.com. "6 Techniques for Account Enumeration in a Penetration Test." https://pentest-tools.com/blog/account-enumeration-techniques-pentesting

  4. PortSwigger. "Username Enumeration via Response Timing." Web Security Academy. https://portswigger.net/web-security/authentication/password-based/lab-username-enumeration-via-response-timing