User enumeration refers to the ability to guess existing usernames in a web application database. This can happen, for example, when using "sign-in/sign-on/forgot password" functionalities of a website.

When an user tries to "sign-in" to a website with an incorrect username/login, the web application should not disclose that the username doesn’t exist with a message similar to "this username is incorrect", instead a generic message should be used like "bad credentials", this way it’s not possible to guess whether the username or password was incorrect during the authentication.

If a user-management feature discloses information about the existence of a username, attackers can use brute force attacks to retrieve a large amount of valid usernames that will impact the privacy of corresponding users and facilitate other attacks (phishing, password guessing etc …​).

## Ask Yourself Whether

- The application discloses that a username exists in its database: most of the time it’s possible to avoid this kind of leak except for the "registration/sign-on" part of a website because in this case the user must choose a valid username (not already taken by another user).
- There is no rate limiting and CAPTCHA protection in place for requests involving a username.

There is a risk if you answered yes to any of those questions.

## Recommended Secure Coding Practices

When a user performs a request involving a username, it should not be possible to spot differences between a valid and incorrect username:

- Error messages should be generic and not disclose if the username is valid or not.
- The response time must be similar for a valid username or not.
- CAPTCHA and other rate limiting solutions should be implemented.

## Sensitive Code Example

In a Spring-security web application the username leaks when:

- The string used as argument of loadUserByUsername method is used in an exception message:

    public String authenticate(String username, String password) {
      // ....
      MyUserDetailsService s1 = new MyUserDetailsService();
      MyUserPrincipal u1 = s1.loadUserByUsername(username);

      if(u1 == null) {
        throw new BadCredentialsException(username+" doesn't exist in our database"); // Sensitive
      }
      // ....
    }

- UsernameNotFoundException is thrown (except when it is in the loadUserByUsername method):

    public String authenticate(String username, String password) {
      // ....
      if(user == null) {
          throw new UsernameNotFoundException("user not found"); // Sensitive
      }
      // ....
    }

- HideUserNotFoundExceptions is set to false:

    DaoAuthenticationProvider daoauth = new DaoAuthenticationProvider();
    daoauth.setUserDetailsService(new MyUserDetailsService());
    daoauth.setPasswordEncoder(new BCryptPasswordEncoder());
    daoauth.setHideUserNotFoundExceptions(false); // Sensitive
    builder.authenticationProvider(daoauth);

## Compliant Solution

In a Spring-security web application:

- the same message should be used regardless of whether it is the wrong user or password:

    public String authenticate(String username, String password) throws AuthenticationException {
      Details user = null;
      try {
        user = loadUserByUsername(username);
      } catch (UsernameNotFoundException | DataAccessException e) {
        // Hide this exception reason to not disclose that the username doesn't exist
      }
      if (user == null || !user.isPasswordCorrect(password)) {
         // User should not be able to guess if the bad credentials message is related to the username or the password
        throw new BadCredentialsException("Bad credentials");
      }
    }

- HideUserNotFoundExceptions should be set to true:

    DaoAuthenticationProvider daoauth = new DaoAuthenticationProvider();
    daoauth.setUserDetailsService(new MyUserDetailsService());
    daoauth.setPasswordEncoder(new BCryptPasswordEncoder());
    daoauth.setHideUserNotFoundExceptions(true); // Compliant
    builder.authenticationProvider(daoauth);

## See

- OWASP - Top 10 2021 Category A1 - Broken Access Control
- OWASP - Top 10 2017 Category A2 - Broken Authentication
- CWE - CWE-200 - Exposure of Sensitive Information to an Unauthorized Actor