40

I'm working on the onboarding functionality of my web app and at some point I'm asking a new user to choose a username.

When the user gets to this point, I have his/her email and a cellphone number that is validated with an SMS and he/she has passed Google's no CAPTCHA reCAPTCHA.

I need to check if the username one chooses already exists and if it does, then the user is prompted to choose another one.

Upon browsing the web, I came across some pages that say implementing such functionality also provides attackers with a tool to check if a username exists.

What are the best practices for checking if a username exists?

  • 4
    do you need usernames or can you just use email addresses? – Neil McGuigan May 16 '16 at 17:42
  • 2
    My app uses usernames, not email addresses as the username (so that the user can later change his email address) – frenchie May 16 '16 at 17:43
  • 1
    It seems to me that one solution is to have them log in with their emails instead of their user names, and simple use the user names on the site itself. – childofsoong May 16 '16 at 18:35
  • 7
    I think email address, when used to log in on a site, should still be called a username. You can easily create another column and call it their contact email. The contact email can be updated at their leisure. At the end of the registration process just say "Thank you, we will send an account activation link to your email." On the backend, send the activation link if the username is unique. If it is not unique then just inform the user that a sign up was attempted with their email and the standard "If this was you then please login, if not then contact us, blah blah blah" – MonkeyZeus May 16 '16 at 19:53
  • 2
    @Mark I believe that would be less user friendly as every time he tries to register, he would be told that the username doesn't exist. Instead of it, asking him to choose a unique username first would IMHO be a better flow – Limit May 17 '16 at 8:03

10 Answers 10

40

This source says that it is almost impossible to avoid user enumeration in this situation and delaying an attacker is the best you can do:

If you are a developer you might be wondering how you can protect your site against this kind of attack. Well, although it's virtually impossible to make an account signup facility immune to username enumeration, it is however possible to avoid automated username enumeration attacks against it by implementing a CAPTCHA mechanism.

However, I might have missed something - I'm also curious if anybody else has a better solution.

  • 8
    Good idea: add a counter to test how many times a user tested the system. – frenchie May 16 '16 at 17:48
  • 8
    Another thing to consider is that at that point in signing up, (past initial CAPTCHA, past SMS and email verifications), they still shouldn't be able to check for a username without attempting to finalize the sign-up process. That is, no AJAX endpoint to say if a username is available, they should choose a username and either get it assigned to them, or get bounced back to try again. – Ghedipunk May 16 '16 at 17:54
  • 14
    @Ghedipunk from a security standpoint that might be good, but from a UI/UX design point it's terrible. I would say a middle-ground of the first $x checks from a given IP are "free" (ajax'ed in), but after that the checks need to be accompanied by a captcha response. By that stage I've already got that username and password in my password manager, and you're telling me way too late that my chosen username isn't available. It would feel like a bait and switch and I bet you would have a high rate of abandoned signups. – Mark Henderson May 17 '16 at 11:18
  • 1
    I suggest explicitly incorporating @frenchie 's idea into this answer. Here's why: if the form starts requiring a captcha after the Nth attempt, and provided that the captcha is doing its job, it not only slows down the attack, it forces the attacker to use a human agent rather than a fully-automated attack. – Mathieu K. May 17 '16 at 17:17
  • 4
    @MarkHenderson: That's not how I read Ghedipunk's suggestion: I took it to mean that, once a user (whose cellphone number's already verified, so we have some guarantee of their identity) inputs their chosen username, then either (a) the username is already in use, in which case they get bounced & must try again, or (b) that username is then permanently associated with that user (based on their cellphone number). Thus they can't use the login process to scan large numbers of usernames for existence: as soon as they hit one that doesn't exist, it's game over (for that cellphone no. at least!). – psmears May 17 '16 at 21:02
18

The alternative to allowing multiple users to have the same user name is far worse!

Really what happens with that system

  • User requests a new password
  • You now have to check passwords to make sure no collisions happen
  • The user can use this to brute force other accounts

That's bad. So you need to prevent multiple users from having the same username. You do this by checking.


What happens if a user can find out another users username

This is the scenario you're presented with, and how do you prevent someone from maliciously using it. What risk do you open yourself up to here?

  • A user can find out a username
  • Still doesn't give access, as they now have to guess the password

Okay good, this offers a little more security and still keeps users from having emails and other sensitive information leaked. But now you want to try and mitigate attacks. How do you do that?

Mitigating registration attacks

There are a couple of techniques that exist here to prevent it, but the best by far is rate limiting and maximum attempts that you log by IP.

Rate Limiting, Maximum Attempts, and IP Logging

When a user tries to register, they can only check for available usernames so fast. In all reality it will take a real person a second or two to type in a new username. This lets you do a few simple checks and balances. However this only really happens when they try to register. When a user attacks you and uses a username that isn't registered, all that happens is they have registered that username. Oh well. Now they have to start the registration process all over again, and here is where you can check.

  • User tries to register a lot of accounts really fast: a bot, stop them and kick them out(rate limiting)
  • User tries a LOT: Attacking by hand, kick them out(maximum attempts)
  • User comes back: Same IP address shows a really determined attacker. BAN THEIR IP(maximum repeated attempts)

Now that attacker has exposed themselves and gotten banned at an IP level. You know there is something bad going on here, have a log of usernames that might have been gleamed, and best yet there was no personal information leakage. Great job!

However now you need to go back and deregister all those accounts that were created in an attempt to find other accounts. Good thing you have those logs!


The Downfalls Of Usernames Without Emails

Here's a big flaw that you need to be aware of when you don't tie an account to an email: The username can be preregistered and held for ransom. It is then extremely hard to prove ownership of the username. This let's someone do a different type of attack against your system where they just attempt to pre register a lot of accounts of people and hold them at ransom. Hopefully you have a log from the above checks so you can try and catch them in the act.


Alternatives

You mentioned they have already gone through the email confirmation process, so another way to handle it at this point is to link it to the email instead. After registration, if they want to change their email they'll have to log in, and send a request with a link to their email to change it. This can be done with a time sensitive token. Now they can have a username too to display on the site. The pitfalls are also mitigated here since the display name can overlap and there is no way to try and gain access to another account with that information, but the email provides a check of truth(and possibly some sort of physical identifier like a profile picture)

  • 2
    The point of enumerating usernames is not only to allow brute-forcing, but also to gain intelligence. "Oh, that Targets has an account at SomeSalesServer? Let's search for vulnerabilities!" or "So, @someman is registered at this dating site ...". That's why you try to prevent this beforehand but few pages do a good job preventing this. But good answer ;) – Sebb May 16 '16 at 19:14
  • I'm sorry, the very first sentence of your answer already seems completely out of context. Why are you suddenly talking about checking passwords for duplicates? That's not what the question is about. Maybe you can include the bridge of conclusions you've made? – Wildcard May 18 '16 at 3:04
  • 1
    The bridge of conclusions is right there before it. In this system, if you decide not to check for usernames, then there is overlap of users to login, and password can be brute forced from a single username for any account with the same username – Robert Mennell May 18 '16 at 3:06
9

When a user tries to log in, he or she enters their username. When registering, you are told if a username is taken or not. If you find a taken username, you can try to log in with it and possibly hack it.

Solution which is also another problem:
If they had to log in with an email, the knowledge if a username is taken won't help - you log in with an email not username so you can't know what to enter in the email text form when you know that there is the helloman username. But now the problem is telling the user if the email is taken.

Solution - when registering don't tell them if the email is taken but tell them that a confirmation email was sent. Send them an email, if the email was registered before send an email saying that someone tried to register their email. If the one who tried to register helloman@example.com is not the real helloman than he probably wont see helloman's email inbox. And if the real helloman tried to create a new account with his already registered email then only he will receive an email saying that he already registered. Unless there is a hacker who managed to hack helloman, but many people like helloman aren't stupid and wont use password as the password, also the most common email systems nowadays are very secure, so it's not to worry (and if he hacked them than it's not our problem(If you setup your own email server for you and your staff)).

3

Allowing a user (or an attacker) to find out whether usernames exist or not is known as a "username enumeration" vulnerability. This is summed up well here:

As an attacker if I can use your login or forgotten password page to narrow my list from 10000 targets to 1000 targets, I will.

You can add "sign-up page" to that list. This aids an attacker in any phishing campaign, or any password guessing attack. It is also a privacy issue if Bob's wife Alice can find out if bob@example.com exists on Ashley Madison.com.

Basically if you are allowing users to pick their own username which is separate to their email, then to guard against user enumeration you should allow login with email/password only, and simply use username as a means of identifying with other users on the site.

This way there is no vulnerability in saying Sorry, username foobar is taken, please select another.

This is because unlike email address, these usernames will not be globally unique in the world. Therefore no user privacy is violated when you say one is taken. As an addition, because you cannot login using the username directly, this would not give rise to a user enumeration vulnerability.

As an extra bonus, this also makes account recovery and sign up a more simple process (which is always good for security). That is, there is no need for "forgotten username" functionality as well as "forgotten password" - as they login with their email address, which they will remember, that's the only thing they need to regain access assuming they still have access to their email account. Make sure you are preventing user enumeration on your email addresses though - see this answer.

As an update to this answer I notice that you say you do not want to use email as a username in case the user decides to change their email address. The only reason that I can see this as being a problem if in your architecture you are using "username" as a primary key, therefore any update to the email address (if it were a username) would cause the need for every linked table to be updated. Re-architecture of this to use independent PKs would be the preferred solution to this rather than making username static. (Noted a big assumption here on my part on your reasons for this.)

  • How about saying that if there have been more than a few incorrect-password log-in attempts with a username, the user must supply the correct email or other non-confidential identification information before any further password attempts will be validated; if too many mostly-correct attempts are made, send a notification to the person's email address (counting only mostly-correct attempts would minimize the danger of someone conducting a denial-of-service attack with random bogus login attempts). – supercat May 18 '16 at 16:52
  • @supercat: I can't see the advantage of letting them login with username at all, when email address is much more advantageous. Also, if you're storing passwords correctly (e.g. bcrypt or pbkdf2) then you have no idea if a password guess is "mostly-correct". – SilverlightFox May 18 '16 at 18:08
  • Many people's email addresses aren't exactly confidential, and using email address alone requires that one either allow unlimited password attempts or denial-of-service attacks. If an attacker would have to guess a combo of user name, email, real name, birthdate, etc. before being able to try out more than a few passwords, that would make brute-force attacks much less practical. Passwords obviously should be hashed, but things like real name, etc. generally won't be, so checking whether someone got 3 of 4 answers correct should not be difficult. – supercat May 18 '16 at 18:24
1

There are good answers from @SilverlightFox and @MaxTheBackspace.

I just want to make one thing clear again: It should not be possible by default at any point in the application to let users enumerate a (likely) global unique identifier like an email address. Not even ONCE. Not with IP address monitoring and rate limiting and a CAPTCHA in Klingon language created with finger paint by a toddler.

This is not only about the security of your service but the privacy of the user.

0

From a user interface / usability perspective, if I've already taken 3 steps (email, phone/ recaptcha), then that reduces the likelyhood of a machine attack, and any further roadblocks will reduce the number of people who want to sign up but give up in frustration. I personally don't mind at all if I try to choose MarkyMark (No, I'm not the rapper), and if some other wanna-be rapper has used that name, I'd far rather have the web site tell me that MarkyMark is already in use (and as others have suggested, also offer the option of allowing them to log in as MarkyMark in case I just forgot that I had already registered 3 years ago), and if the site then suggests I try MarkyMark345 or MarkyMarkyMark -- that doesn't necessarily give away that there are MarkyMark1 thru MarkyMark344 already in use. I appreciate the suggestions. In fact, once a site suggested MarkyMark78 and I tried MarkyMark77 and was successful.

  • Without some kind of further captcha-upon-attempt-threshold, though, OP's still exposed to username harvesting at this point, right? – Mathieu K. May 17 '16 at 17:09
  • Probably; but if someone is that determined/patient, they will try anything; so like anything, it is a trade-off between annoying customers vs. thwarting crackers. – Mark Stewart May 17 '16 at 17:54
  • 1
    Allow me to rephrase: if I have a burner email address and a burner phone, and provided OP doesn't have some kind of further captcha-upon-attempt-threshold, I can now harvest his entire username list with 5 minutes of proving I'm a human, and whatever time it takes to set up a machine to do the rest. – Mathieu K. May 17 '16 at 18:03
0

Enumerating usernames is a threat because the combination of username + password will give an attacker access to the account/system.

How much of a threat that is depends on your system. For example, in a forum where the username is anyway attached to every post, that threat is very low. If you enforce strong passwords, it reduces the threat.

In your scenario, due to all the data you checked before he comes to that step, enumeration would be incredibly costly, so stop fucking your brain and just go ahead and tell him "username taken", with alternatives offered as suggested by Mark Stewart.

0

It may be a better practice for the login process work with an email and not a username. If you need to have a username by not making it a piece of authentication an attacker can no longer use the username availability check as a malicious tool. At this point the username is just vanity and public facing identity.

I know in a comment you stated you wanted your user to be able to change their email so you didn't want to use this for logging in. It is a completely acceptable practice to do this and allow the changing of the email address. The very site you are on does this the same way.

So now you have the issue of checking to see if someone already registered with an email address. For starters it is much harder for someone to guess someguy54@emaildomain.com over someguy. And then if the attacker knew of someguy on your service and wanted to attack him specifically he would have to know the email he signed up with along with his password opposed to just a password.

But automated attacks and persistent manual attacks do happen. It is very hard to tell if someone tried 50 username combinations is looking for information or just trying to find the perfect username they legitimately want to use. But if someone is trying to sign up using 5 different emails it is a much bigger red flag for abuse.

So by switching the authentication to email over username you get:

  • No public facing authentication information
  • Harder to manually guess information
  • Easier detection of abuse

The downfall is a potential privacy leak like mentioned by Lukas in the comments. If someone wanted to know if a certain user was a member and they knew what email they would likely sign up with they could cross check it with your registration system.

  • 1
    Not necessarily true. If I want to allow several usernames for the same email, the username system works better, and with emails you run into the exact same problems, only this time you've exposed an email. Now they can check for emails that have been pre registered using the exact same method. The truth is you have to know their email or username already to see if they've registered to check if they've registered. Otherwise there is just an account registered to that email or username. The difference is that username based accounts can be held for ransom, while email based just registers them – Robert Mennell May 16 '16 at 18:35
  • 2
    One thing to consider: I'd rather have my username enumerated than my email address. (From a privacy perspective - think Ashley Madison). – Lukas May 16 '16 at 18:42
  • 1
    e-mails are used as logins mostly for convenience reasons (people remember their e-mail better than 20 different usernames), but they make it easier to guess usernames, especially if you have a large database (from one of the many leaks, say) of them to try. Seperating display name from login name, however, is good advice. – Tom May 17 '16 at 9:16
0

This isn't generally a serious concern unless your usernames are email address, which would permit the spamming or phishing of your users. (Or also potentially if you provide a messaging API.)

That said, if you feel like the underlying username needs to be a secret: Let new users choose a display name, but don't allow them to choose their underlying username. Instead, either offer OAuth or append a random number or word to their display name to use when signing in.

And even then, if you do have a messaging API, the more clear-cut protective measure is to ensure that messages are clearly marked with the originating username.

-4

Well, the first thing I have to say is what I say to just about anyone asking username/password questions: stop that. Use OpenID and get out of the username/password business.

Secondly, usernames should be looked at as a hash collision mitigation, not as security -- i.e, two people with the same password. From a security POV, you should consider the username as one of the elements an attacker already knows, not as additional security.

So, I would suggest allowing email and possibly telephone numbers as well as usernames to identify an account where they either can't or won't use OpenID (although really, I'd force it's use by setting up my own internal provider as as a second app).

Finally, when you do have to take a username, rate limit the submissions. Take a minimum of two seconds to return the answer and double the time for each attempt, require going through the whole process again if they abandon the session without picking a username.

protected by Rory Alsop May 17 '16 at 20:23

Thank you for your interest in this question. Because it has attracted low-quality or spam answers that had to be removed, posting an answer now requires 10 reputation on this site (the association bonus does not count).

Would you like to answer one of these unanswered questions instead?

Not the answer you're looking for? Browse other questions tagged or ask your own question.