>First, the client makes an unauthenticated request to the server to retrieve the password salts associated with the username. If no user is found, an error is returned to the client. If a user is found, the server sends the client the user's password salt and password token salt, which the client uses to rebuild the password token. The password token is then passed to the server for authentication. To prevent brute force password guesses, clients get 25 incorrect attempts in a row before the server locks the user out of their account for 24 hours (Note we are aware this introduces a DoS vulnerability. Our first priority is to protect user data. We plan to implement a more sophisticated lockout mechanism in the future).Hang on, so the process of retrieving the salt gives the remote client information about whether the user exists? Doesn't this mean that an attacker could take a list of possible usernames, and confirm which of them are using your service?
Seems like you could return a salt even when the user doesn't exist, and that would prevent this information disclosure.
j-berman|5 years ago
Additionally, practically defending against user enumeration beyond rate limiting sacrifices a level of security and privacy (for example, by requiring users provide an email to sign up to your service, or through some other means that likely ties the user to an identity and storing this in our database in plaintext), rather than allowing them to use pseudonymous usernames alone.
While we do recognize username enumeration is an issue (because users tend to reuse passwords from other sites, or don’t want to be found out using a site), we concluded that properly defending from user enumeration by default would have too material of a negative impact on user experience for little gain on top of what we already provide in way of protecting user data, and instead focused on defending against potential follow-up attacks by limiting brute force login attempts, and recommending that you tell your users to use a password manager at sign up.
The most significant place defending against enumeration affects is during sign up. When a user’s account already exists, we say the username is already taken, which isn't possible when properly defending against enumeration.
We're planning to allow you to enable email verification in your app if you want to, so users will need an email to successfully create an account. Once that's in place, we'll defend against enumeration more concretely. There are other places in addition to the salt retrieval that would be modified in similar fashion. For example, password reset will need to always successfully return even if a user provided the wrong username, and sharing a database with another user will always successfully return even if the other user doesn't exist (e.g. from a typo).
zaroth|5 years ago
You want to focus on implementing good soft and hard rate limiting on all your endpoints.
You can obfuscate the login function to return an unhelpful error message, but unless you harden every possible public API against user enumeration — and most sites do not - you are just hurting the UX for no actual security gain.
This would include constant timing for returning results when there is or isn’t a user, so for example, running your hash function even when you don’t have a password to compare it to.
Years ago there was a big push to return unhelpful error messages, but then the signup or password reset functions would act as a user exists oracle anyway. Login got harder for zero actual gain in security.
nickodell|5 years ago
zelon88|5 years ago
Security isn't about one feature. It's layered. You need to have layers because there is no such thing as guaranteed security.
Bank safes are my favorite analogy. Safes are given a time rating. "How long can this safe resist being broken into." A bank with a 15 minute safe means that it might take an attacker 15 minutes to open the safe.
A 15 minute safe is not secure. Infact it is guaranteed to be compromised past 15 minutes. How do you secure an insecure 15m safe? With a 5m guard duty. Now you have a safe to buy you 15 minutes and a guard to ensure that nobody has 15m worth of access to the safe.
You built a safe with no guard... and by allowing enumeration you're telling attackers where you put the safe. You are almost guaranteeing someone will compromise it eventually.
Security doesn't always mean that successful attacks are impossible. Oftentimes security just means you've made the cost of intrusion higher than the return on investment. If you allow enumeration you're giving the attacker an advantage.
j-berman|5 years ago