“Invalid username or password” is a useless security measure
kev.inburke.com
kev.inburke.com
Always send an email.
If that user already exists, make sure the email says "We noticed you're trying to sign up again. If you didn't do this, someone else is trying to sign up for you." If that user doesn't exist, send them the typical signup message.
The author has some great security tips in the "what should I do instead" but I don't think the instead portion is accurate. Keep private information private, and make access to public information as easy as it should be.
If emails are public, shoot, use a websocket connection to style the login field in real time and show the user whether or not the email they typed is valid.
The email will be sent regardless. If it's a valid new signup, then the user gets a transparent experience. If the user has already signed up, send them a helpful "hey, you've already signed up" message in their inbox.
In case it was an attacker, maybe check the IP against known IPs that have had valid logins. If it's a new IP, add a section to the email message that a potential attacker may be trying to login and that no action is required, etc. Whatever fits your business case.
This process also won't work for the small minority of sites that don't require email adresses. HN is one example.
And the entire point is moot for most social sites as it is trivially easy to check for the existence of a user by going to their profile page. Why bother going through all this email trouble when I can just go to twitter.com/TheUsernameIWantToCheck to see if that user account exists?
One possible solution to that particular problem: https://news.ycombinator.com/item?id=8683589
> Why bother going through all this email trouble when I can just go to twitter.com/TheUsernameIWantToCheck to see if that user account exists?
I totally agree. Twitter usernames are hardly private information, though. As I said, make sure truly private information is kept private.
Keeping in mind the UX, though, most users may not realize how easily their user id ( email/username, etc.) can be discovered. Seeing the login screen confirm that their username exists but their password is incorrect may in fact scare existing users away.
Determine what's best for your demographics, and ideally A/B test the heck out of it.
The email should still indicate new or existing user, and provide them a link that they can use to associate said UUID with their login and pull all the data onto their account as if they'd been logged in the whole time.
They'd need to be informed that all account data is accessible only on that computer, at least until they've confirmed their email address. I kind of imagine this is the existing behavior for many applications, though.
Additionally, if the service doesn't require emails to be authenticated, then they shouldn't be using them for much more than account recovery or notifications (once authenticated). Otherwise, I can sign up for that service with somebody else's email as long as they haven't signed up before, and then if that person ever wants to sign up for this service, either they're out of luck or the original (perhaps misguided) customer is out of luck.
Well put. Im also a fan of 'If this wasn't you, click here' link within the email to more positively ID the request as malicious
Of course, this all goes out the window if you're not using emails as usernames.
The difference for usernames is that they may or may not be shared across services. They're a bit more transient to the average user than an email is, though only a bit.
That said, it depends more on your use case than anything else. If any information is private information, it should remain private. Otherwise, I say make it easier to access that information, and be consistent.
To be fair to those implementing the example login systems as well, not all malicious attackers are hackers. It could be an angry ex who wouldn't necessarily think to try creating a new user before proceeding with their password-mashing attempts.
"Bad login combination" and variants do avoid information leakage. It's just not consistently enforced across all surface area. As another example, I believe Amazon public wishlists are searchable by email. That alone can net you an easy list of logins to brute force or check against existing email-password lists.
I think it's most likely that these services are avoiding the "that username exists but it's not actually yours" conundrum while keeping code (and UX) complexity to a minimum.
Of course, aside from my own projects, the only service I've seen that exclusively does this is Fancy Hands.
The problem is that Facebook totally mismanaged their apps when they first launched, and a lot of people were permanently turned off to "Connect to Facebook" functionality by random apps posting under the user's name.
My account was recently abused by Glassdoor, which fervently promised not to post anything to my friends, and then immediately did so.
This adds more friction in the signup process than I'd be willing to accept, but it does solve the information disclosure problem.
I have a hard time seeing what friction is added, though, with proper thought. If you don't require email validation but use it as a unique login identifier, then a malicious body could DOS all future users via bulk signups. DDOS required if you use rate limiting on signups. Slightly different application of the attack but still a denial of service.
Even a non-hacker could block another user from signing up by simply creating an account with someone else's email address.
If you don't use it as a unique login identifier, there's no problem at all. New users use their username to login.
Hotmail do this everytime someone tries to reset my password which happens almost daily as I have what turns out to be an account name that is in demand and it's really irritating. And there is nothing I can do to stop someone attempting to reset my password - I have 2 factor switched on so they won't be successful.
Of course you can add more logic in to try and be more intelligent about when the emails are sent, but most sites won't get this right when they can't even store passwords correctly.
As the original author noted, rate limiting is also a fundamental requirement for security. Eventually no more emails are sent because the offending IP addresses are effectively blocked.
Make email notifications an option for users (enabled by default, with an easy link in the email to disable), and you ensure your users that your service is secure, while giving them the ability and easy path to ignore it.
This is important. It is not always necessary or appropriate to try to maintain the secrecy of otherwise public, directory information (like email addresses). Unless you are, say, FetLife, it doesn't actually make that much sense to hide the fact that joe@example.com has at one point signed up for your service.
On the other hand, associating joe@example.com with posts he thought were anonymous is an enormous violation.
Email is found, password is not correct. Email unknown.
Consider the case where you mistype your username (email). For sites like amazon, gmail, hotmail, yahoo, twitter, etc, it is entirely likely that the mistyped username is somebody else's valid username, you typed the password correctly, and "incorrect password" would hide the problem.
Here's what I tell the developers I work with: Design for the users, not for development ease. This pattern has been successfully implemented by literally hundreds (thousands? hundreds of thousands?) of websites, so there's no excuse for trying to cut a corner and try to concatenate use cases. Either the user name is right and the password is wrong, or the user name is wrong and the password doesn't matter, or both are wrong and through some miracle of absolute unicorn user error they've managed to log in to someone else's account - at which point you have a different issue altogether.
I think a "did you mean?" output in case of a bad password as long as there are lexically similar usernames in the database.
banks and yahoo handle this case by showing a magic image that you chose, which also provides anti-phishing feedback
"Password does not match our records for username/email XXX. Please check that both password and username are correct."
Responding to below This reads to me like a more verbose version of "Incorrect username or password."
Note this version is not wordsmithed and could probably be done better but the goal and inherent difference of this to the original is to let the user know that the username exists and that its the password which does not match.
Well, a lot of people do not know their own phone number.
No, it is usually incorrect and it is much less helpful than identifying which is incorrect.
"it is entirely likely that the mistyped username is somebody else's valid username"
No, that's highly unlikely.
If the email isn't in the DB, just say so. If the password doesn't match, just say so.
If either the username does not exist, or the password does not match the existing username, then "incorrect username or password" is correct by logic. It might be incorrect if assuming xor meaning, because both could be wrong -- username and password.
The parent's point also was that the service can not identify which one is wrong. Was it the username if the password did not match but matches any other account?
> No, that's highly unlikely.
Why should it be unlikely to mistype the username? Misspellings happen. Some people check what they entered, some don't. People confuse their usernames when they have many. One username might be someone else's on another service. Etc.
> If the password doesn't match, just say so.
Which is wrong if the error lies with the username, not the password, and the mistyped username happens to exist.
It's unlikely that a typo'd email address is in the DB.
I suspect there's a lot of poorly written software that still does stuff like this. The message is still valid in these cases as well.
:P
While this is true, it's perfectly reasonable to require a captcha before allowing a new account to be created; greatly limiting the speed at which an attacker could enumerate emails. While it's not going to stop targeted attacks, it will mitigate mass brute forcing of weak passwords.
Regardless of the ease of username enumeration, all of the author's points about what to do are great for most sites. Rate limiting with exponential backoff and 2fa are some of the cheapest and most effective means of increasing the security of your app's authentication process.
Unless you delay all failed attempts to login by a random time of 500-2000 ms, it's unlikely you'll see much improvement in response rates... having such a random delay is probably helpful anyhow.
e.g. Twitter can't require captcha before letting me see twitter.com/username …but they're still doing this security theater.
(Altough, some real rate limiting is better, except when dealing with a distributed bot. Maybe both.)
Consider the case when the primary usernames are always emails (many sites do this), and signing up for an account is simply done with entering an email and a password. Then, when someone submits a signup form, the website can:
- Check if an account with the email exists, and if it does, whether the given password matches the existing one.
- If both are valid, log the user in, optionally showing a message saying “there was already an account with these credentials so we logged you in”
- If an account with the email does not exist, or if it exists and the password doesn’t match, return a message to the user saying “please check your email and follow the validation link”. The user can’t tell if the email exists or not.
In the backend:
- If the account did exist, send an email to the user saying “someone tried to sign up for an account with your email, please let us know if it was you. and here’s a way to reset your password if you forgot it”.
- If the account did not exist, send an email verification link, which then redirects to a page to complete the user signup
Same with password resets, the success message can always say "check your email".
There are ways around revealing the username. But I agree, only doing this in the login page is useless.
Oh hell no.
First off that is completely ineffective. Botnets are common and inexpensive. But worse still a lot of users often share a single IP (e.g. university dorms, businesses, public wifi, etc).
I agree with the first part of this article (i.e. that it is trivial to "prove" a username is valid, and that worse error responses aren't accomplishing anything). But that advice is poor.
> Check submitted passwords against a dictionary of common passwords (123456, monkey, etc) and ban that traffic extra hard.
That seems like a lot of engineering work. Plus the workload of loading up a dictionary and doing a lookup upon each login is resources that frankly could be better used on a slower hashing algorithm (to slow logins and make stored hashes stronger).
> Exponential backoff (forcing attackers to try again after 1, 2, 4, 8, 16.. seconds) is useful as well.
Just open a spreadsheet and determine how long it would take to test 1500 passwords. 1500 is a "short" dictionary of common passwords. Even a 1 minute delay after each 2 attempts (or 2 minutes after 4, or 4 minutes after 8, etc) means 12.5 hours of attempts.
I like to set the attempts really high with an equally high lockout. So no normal user will see it. Most users do a password reset after the fourth or fifth attempt, so if you set it on 8 attempts with a 4-6 minute lockout then the majority of users will never run into it.
Instead of focusing on these kind of hacks why not just:
- Set no maximum password length (250+ characters)
- Set the minimum to at least 6 or 7
- Get a password score widget (e.g. "weak" "normal" "secure"), traffic light, to encourage better behaviour
- When a password is set check it isn't in a common password dictionary (a lot of score widgets integrate this!)
- You can use Javascript to check a password dictionary as it isn't a site-security feature, and anyone who goes out of their way to bypass it only has themselves to blame.
- Agreed on 2F, Google Authenticator is trivial to integrate. The only remotely "hard" bit is generating the QR code and a lot of libraries exist for that express purpose.
- No clue how you'd integrate LastPass...
- Account Alerts are extremely useful. As are admin alerts. Too many sites get no warning when a lockout occurs, it is pathetic.
> When a password is set check it isn't in a common password dictionary
I agree with this with short passwords. But for passphrases? No. I find it rather frustrating that (ex) diceware passwords are often rejected as too insecure or flagged with a nasty warning.
I too have ran into that issue. Instead sites doing:
password.Trim().Equals(<dictionaryItem>);
They do: password.Trim().Contains(<dictionaryItem>);
So if you do a pass-phase or full sentence they'll call your password insecure for being a common dictionary word (even if it is 20+ characters long).That's why score systems are better overall than any one single check. All parts of the password should be considered (e.g. length, complexity, commonality, etc).
Why not allow the unlocking of an account through email and 2fa?
An email like, "Hey, we've locked your account because we've received a ton of incorrect login attempts. To unlock your account click the big button below OR visit www.example.com/longrandomhash"
And then have the user login again (and potentially use some sort of 2fa like facebook/github/google code generator).
On that point me and the article are in complete agreement.
It's important to note that if you take the classical advice to "use bcrypt" (http://codahale.com/how-to-safely-store-a-password/), your password will be effectively truncated at 72 characters.
I still wouldn't limit a user from entering in a longer password. I'd display a warning if they attempt to enter in a 73+ character password to inform them that passwords longer than 72 characters offer no additional protection.
> Just open a spreadsheet and determine how long it would take to test 1500 passwords.
5.557 * 10^443 average Gregorian years.
* Concurrency -- I can check a lot of passwords at once. Are there race conditions in the backoff? Can I check 20 in one second? 200? What about all 1500? (yeah, that's not subtle and alarm bells should go off).
* Alternately, one can iterate usernames against common passwords, especially if usernames are known. (and as a subset of this, if accounts are locked after n tries and usernames are iterable, that's a DOS waiting to happen)
I think websites say "Bad combination" not because usernames are treated equally with passwords, but because you don't have a choice but say that.
If I tell you that your username is incorrect, am I telling you your password isn't? This would be silly, because if the website is new and I know a password is correct, then I can either find the username out there (if the website is social), or pretend I forgot my username and have them give it to me.
Assuming that's not what I am saying, then the user is surely to have a bad experience anyway, since they will need to figure out a wrong username, and then in the worst case, a wrong password. When you say a "bad combination" you at least eliminate a possibility to mislead them into thinking only one of their credentials is wrong.
I have a number of sites I don't use often, that I wind up having to do a password reset on to then find out I'm not even using the right username... there was literally no gain from this... Any hacking attempt can do the same to determine if a username was valid or not.
It's making things easy for machines to do harder for people to do, which is the wrong approach to security.
Of course you do.
> If I tell you that your username is incorrect, am I telling you your password isn't? This would be silly
Of course it is, a password is checked against a username, not against the whole database. If the site is telling you the username is incorrect it's telling you just that: the username is incorrect. It wasn't able to go any further and check the password since it has no idea which password it should check for (or even, if you're correctly storing passwords, which salt it should use for the password check). Your criticism doesn't even make sense.
Consider that on any decently sized website, you're going to have a lot of cases where someone's trying to log in and they typo their username into someone else's username (e.g. if you tried to log in as "masklin" and that was taken). This looks to your server exactly like a wrong-password, but it's not.
If there's genuinely no user by that name, sure, tell them.
Not exactly. Multiple users could obviously have the same password but not the same username. Telling an attacker that the password is correct doesn't narrow down the possible user names it could have, <n, thus still meaning the attacker must theoratically try all valid username combinations with that password unless they have some way to narrow it down.
ryan@gmail.com -> ryan@gmaail.com probably would have some benefit to UX in saying "email address does not exist", but ryan@gmail.com vs. ryanl@gmail.com both exist.
What I'd probably recommend instead, if you do decide confirming account creation status makes sense, is judging by cookie or IP. If 10.10.10.10 has previously logged in as ryanb@gmail.com, and I enter ryan@gmail.com instead, it might make sense to poke on the email; if I enter ryanb@gmail.com and have an incorrect password, maybe suggest bad password. (of course, if you have a cookie, you might as well pre-fill the login; if you have an IP match, probably not).
The security tradeoff here exists but isn't huge. There are definitely cases where the user benefit (and thus reduced bounce rate) of suggesting error in username or password would make sense. The biggest security issue is confirming "does this account exist at all?", because email addresses tend to uniquely identify users, and you need some other mechanism to prevent this -- either automated (captcha would work) or targeted (in which case it's quite hard).
I'm actually in favor of per-action security checking vs. "logged in or logged out, all or nothing".
Whatever you call it, it's good to keep in mind that Google, Apple, Facebook, and Twitter are vulnerable.
The most interesting facet to user enumeration is privacy. If Alice knows Bob's e-mail address, is it right that she can easily check with Carol's web system to see if Bob has an account with Carol? This is like calling a hotel and asking if a certain person is staying there. It leaks information confirming that two parties have a relationship.
What makes the privacy aspect especially interesting is that user enumeration is a bigger problem for small websites than for the Googles and Facebooks. This is because it's not much of a privacy breach to be able to test if a given e-mail address is on Facebook; big whoop, almost everyone is. But it's a much bigger breach to be able to test if a person is a customer of an illicit service. Most systems sit somewhere between those two extremes, and each needs to decide whether to prevent user enumeration.
The main point the author tries to make is quite matter-of-fact: If you are going to try to prevent user enumeration, then you have to do it for real, which usually results in less friendly account creation and password recovery systems.
This can be easily fixed by sending email conformation tokens prior to creating the user account and informing users in this email, in case they already have an account.
Often, you will login with a username, but the password recovery form will only accept an email. You might be able to correlate these two if you have a lot of data from other, external attacks, but unless if I noticed correlation between recovered passwords and account break-ins, I wouldn't worry about this.
There's a security tradeoff, but sometimes security must be risked in the name of functionality, or you'll have a lame product. Or, you can have both security and functionality, but to the detriment of UX.
Take leaderboards for example. If you have a leaderboard for your app/game, you'll expose user's username (and thus, sign-in name, as is often the case). You could mitigate this by: having a separate login name (ala steam), or using an email (which exposes people to the recovery exploit), but it doesn't make the UX any better.
In the end, I'd say trying to protect against username/email guessing attacks is probably unnecessary. There are better ways to approach security.
RFC 7231[0] suggests something similar
"An origin server that wishes to "hide" the current existence of a forbidden target resource MAY instead respond with a status code of 404 (Not Found)."
with RFC 7235[1] suggesting the use of 403.
"A server that receives valid credentials that are not adequate to gain access ought to respond with the 403 (Forbidden) status code […]."
[0] https://tools.ietf.org/html/rfc7231#section-6.5.3 [1] https://tools.ietf.org/html/rfc7235
> RFC 7231[0] suggests something similar
Well no, RFC 7231 suggests that rather than telling an authenticated user he does not have access to a resource you can tell him the resource does not exist at all. It has nothing to do with the authentication itself, and certainly isn't suggested (let alone recommended) as a response to an invalid authentication attempt.
> valid credentials that are not adequate to gain access
How does a clear statement that the user's credentials are valid but don't give access to a resource have any relation with the rest of your comment?
But, if you're trolling for lots of users, the "new account" feature will have a much lower operational tempo than the authentication workflow, and for a privacy conscious organization, you can do things to make it harder for attackers. Examples: Captcha, data input validation, risk scoring, don't provide immediate confirmation, etc.
Revealing that the email address is a valid system account isn't a particularly useful piece of information to a user who isn't remembering a password. john.smith100000@gmail.com is probably taken by another John Smith. It just isn't a useful piece of information.
Imagine you put your email on your cv and this is done to see if you a member of a democrat or republican website, and you are not offered a job based on your political views.
Imagine that you use your email to sign up for a government service and they take that email, do as described above, and use the information in the future to discredit you in some way.
Maybe I have missed the point, but I personally think that this is a also privacy issue and only looking at it from the perspective of UX may have undesired consequences for people.
I agree with you that the thing to do is fix those issues, though, rather than abandon it here as well.
I do think there is a privacy issue, but I think it is rather minimal and as the sibling poster states, the email address is already exposed.
This rule doesn't apply well to say the majority of b2b systems that don't have account creations public. Thus now I can phish for users and send them targeted oh reset your password here emails because I harvested them from some other hack. Remember it is not always the password that is the weak link it is the user as well.
So calling it useless is shortsighted, yeah for many basic sites it is simple to say yeah tell them what is wrong. That is why they created the picture login to go with the email to ensure you are logging in on the right site. If anything you aren't sure try forgot password with your email.
Source: Real experience building auth systems for large corps.
Send email to email address on file stating "someone has attempted to log in to your account multiple times..." only after multiple unsuccessful attempts.
What about a flat rate-limit of something like 30 seconds (or something similar) which is low enough that a user likely wouldn't be that irritated by it, won't be a severe DoS and is likely sufficient enough to hinder illegitimate login attempts?
On the other hand, this is a reasonable argument for prompting on invalid user names, e.g. "This doesn't appear to be an existing account, make sure you typed it correctly, or try our [sign up page]".
Usernames, yeah, you're not going to keep those private. But sometimes account names are not the same as display names, and display names can be duplicates, while account names should be reasonably protected.
What this really highlights is a lack of consistency. If adding a security measure involves some kind of trade-off (UX in this case), you should really understand what you are trying to prevent and consider the rest of the attack surface. I think it would be a fallacy to immediately give up just because a larger vulnerability exists though.
I was curious to see whether there is a way round this and a quick search threw up https://security.stackexchange.com/questions/40694/disclose-... - the top answer is quite informative.
That's terrible UX.
> But there's a tradeoff there between security and UX, I hear you say. I am trying to show you there is no tradeoff; you are choosing between a better user experience and a worse user experience.
I didn't reach this conclusion at all. Am I missing something?
I think the goal when the User is a malicious attacker is for them to have a terrible UX.
An attacker who wants to find out of if the person with "example@example.com" has on account on your website, could try signing up with "example@example.com". If they get an error message, they know the person has an account on the website.
Your complaint is extremely unclear. Could you elaborate? Are you suggesting that websites should allow multiple accounts per email address as a matter of course?
He's saying that letting the user know the password is wrong, and not the email address, is a better error message. Since an attacker can simply try to register a new account to check if an account exists.
If a site/service needs to hide user names then it needs to do a better job with signups. It's possible to hide user names / emails during signups too and say: "if you already have an account you'll get a reset password link otherwise welcome message from us".
May be behind this the author put some good deeds, but in the end I think it's overkill.
No it doesn't. It assumes, correctly, that that involves a bunch of extra work and that obfuscation raises the economic cost of an attack.
You're taking something that is easy to automate and using it as a solution that makes it harder for people to use.
How many times have you been to a site you haven't used in a while to try several different passwords, only to hit the password reset form and discover the username wasn't even correct? How many minutes of your time were wasted? How much security was actually added?
The answers are... Yes. Too much. None.
Also signing up usually involves more than a login/password (address, phone no., etc)
Yeah, someone could do something like "you'll get an email if this email wasn't registered already" on sign up
No. Using some features may involve more than a login/password, but even on most ecommerce site the signup itself is just a login and a password. Amazon needs a name (whatever), an email and a password. That's it.
There's lots of things you could do.. but displaying an obtuse error doesn't add to usability. I was talking about the password recovery screen, not the signing up.
The "you'll get an email..." is a message I've seen.. and had to deal with it being broken (the email provider the site used was overloaded/down) .. no email.. more broken usability...
Needless to say, this caused some confusion for users.
The rest sounded okay but that seems to indicate that the author probably hasn't tried to solve the security problem on a busy site.
"We are dumb enough to force your password to have at least one Uppercase/Lowercase/Number/Symbol and it must be at least 8 characters long, but you can't have consecutive numbers, this may make it different from your usual password pattern and force you to recover the password every goddam time you try to use our site".
The error shown isn't to dissuade people from using web pages to try to gain access to accounts - it's because the raw code itself doesn't know which is which, and writing code that does enables fast-paced timing attacks.
I don't follow. Surely the reason to hide usernames is not to prevent a timing attack that would reveal... usernames.
Correct username, correct password: takes 30ms to execute the code
Correct username, incorrect password: takes 15 ms to execute the code
Incorrect username: Takes 7 ms to execute the code.
You fuzz usernames, you get one that takes 15 ms, you know that's a valid username. You then start working the password.
Not necessary on most systems, because we're working at speeds that are measured in nanoseconds, and since we're using networks for many attacks, the delays are unpredictable and measured in (at least) milliseconds.
If anything the timing attacks weaken the argument against hiding usernames.
Timing attacks are solved by how you implement the backend checking code and not how you present the result to the end user in the most user friendly manner.
SELECT "Id", "Hash", "Salt" FROM USERS WHERE "Email" = $input
if (results.length == 0) return -1; //No record, bad user, return early...
if (results["Hash"] != Hash(pwd.trim(), results["Salt"]) return -2; //invalid passwordFor that matter, adding a random 500-2000ms timer before returning a failed result would likely be as effective, and not lead to a bad user experience.
I don't know if there's a solution to that in the general scheme of things, other than making the variance of query times between no user and some user as small as possible.
IMHO usability is more important. There are other ways to improve security. Rate limiting with < N failed attempts via an IP in under < X minutes.
Network latency variance just means you need to sample more. It doesn't prevent the attack.
http://blog.astrumfutura.com/2010/10/nanosecond-scale-remote...
This is not done because of timing attacks, it's done because of cargo-cultism and laziness.
> The error shown isn't to dissuade people from using web pages to try to gain access to accounts - it's because the raw code itself doesn't know which is which
Modern systems store passwords salted and hashed with KDFs. The stored hash must be retrieved before the provided password can be checked as you need the stored hash's work factor and salt, which are stored alongside (/as part of) the hash. Such a system necessarily knows which is which.
https://passwordless.net/ - Token based authentication