When performing security testing on a website, one of the first things I often check for is username enumeration. The purpose of username enumeration is to gather a valid list of usernames for a target website, with the idea that those usernames can later be used in targeted attacks, such as password attacks or social engineering.
To enumerate valid usernames, the high-level idea is to submit different requests to the web application and look for differences in responses. These requests are typically made at the login page or the forgot password page, but can also be made at other places in an application. Looking for differences in responses is usually visual, but can also include the response timing of the application as well. Here are five methods for testing username enumeration:
- Visual Testing (intentional and overtly obvious)
Occasionally web applications will plainly tell you when a user doesn’t exist. For example, if I attempted to log into Outlook[.]com with an account that doesn’t exist, I’ll receive a message saying the Microsoft account doesn’t exist:
However, when I enter an account that does exist, it will respond with a password prompt instead of an error message:
An application like this will make it quite easy to enumerate users, however many applications are not this obvious, and instead opt for a generic error message specifying that either the username or the password is incorrect.
- Visual Testing (not obvious, subtle change)
In this example, the application provides a generic error message regardless whether the username is valid, however, subtle changes to the username field are made if the username is valid.
The following shows a login request and response with an invalid username:
When a valid user is used, the error message stays the same, but notice the subtle change in the response:
Hint: The username returned capitalized if it was valid.
You’ll also occasionally see applications where invalid usernames disappear from a form after an invalid login, while valid usernames will remain on the form. Any change in the response is worth investigating.
- Visual Testing (not obvious, requires viewing the source)
In this example, the application provides a generic error message regardless whether the username is valid, and there are no subtle visual changes in the application’s response.
An invalid user will be presented with:
And a valid user (with an invalid password) will be presented with the same error message:
With applications like this that opt for a generic error message, occasionally the HTML source will reveal differences that are not displayed to the user. In this particular web application, viewing the source for the invalid user revealed this:
While viewing the source for the valid user revealed this:
Tools such as Burp Comparer are great for this, as it will automatically highlight any differences between responses.
- Time-based testing
Differences in responses are not always visual. Occasionally you can determine username enumeration through timing. For example, an application that took approximately 10 seconds to respond for an invalid username, while a valid username responded immediately.
The screenshots show the login on the left, and Google’s Timer application on the right, measuring the time it took for the application to respond:
You can test the response times using Burp Repeater or Intruder. Contrary to the example, there is normally not such a large gap in response time. It will often be a narrower delta, and you may need to run the repeat the requests multiple times in Intruder. The usernames that consistently show an abnormal response time are likely the valid ones.
- Non-login page enumeration
If you happened to be testing an application that is impervious to the previous method, you may want to try the same type of methods, but not on a login page. The typical pages I would look to test are: Forgot Password, Forgot Username, and User Registration. These pages may be more user friendly than the login page, and are more likely to give you clear indication of whether or not a username exists.
For example, a login page my present a generic error message regardless of the validity of the user:
However, when visiting the ‘Forgot Password?’ link, it overtly let’s you know whether a user is valid or invalid.
At the end of the day, username enumeration may not be something the website owner cares about, and that’s fine. Generic error messages may be good for security, but they do affect user experience. As an attacker though, I definitely care about username enumeration, and hopefully you’ll find these methods useful.
This post is not meant to be all encompassing. All websites are different, and I’m sure many more ways exist to enumerate valid usernames.