bcrypt hashing vs comparing on user login

Viewed 328

First, I apologise for the question. I know there are a ton of similar obvious questions that ask for how to implement bcrypt in X app, but here I'm asking between 2 strategies.

I just recently implemented bcrypt into the user login. Basically:

  • User sends credentials to my rest api server
  • I bcrypt.hash the given password and compare it to the database with a simple where clause as the following:

WHERE mail = $mail and password = $my_just_hashed_password

  • If I find a user on the database I assume that the given password is correct. Otherwise, I reject the request.

This is almost the same as first selecting the user from the unique mail given and then bcrypt.compare the given password and the hashed one from the database.

My question is, which one is better? I know that the bcrypt hashing function is intended to be used to safely store sensible data, but why not using it to also validate users?

2 Answers

When you use bcrypt to hash a password correctly, you append a string of at least sixteen random characters to the password and run the whole thing through bcrypt many thousands of times. It should take at least a quarter second to complete the hash algorithm. Then you store the random. string, known as a salt, along with the hashed password.

To verify a password, you extract the salt from the stored password hash, then hash the password you're verifying along with the salt. The two hashes must match.

Because of the salt, there's no way you'll ever be able to compare one hash to another and expect them to be equal.

Read this, please. https://en.m.wikipedia.org/wiki/Bcrypt#Description

Why is this so complex? Because cybercreeps. Because cybercreeps sometimes can steal the users table for a large web app. It is bad enough that they steal the usernames and emails, but we don't want them to steal the passwords too. See Adobe. See Ashley Madison.

Without the random salts, it's possible for attackers to construct lookup tables to help guess passwords from hashes: RAM is cheap these days. The random salts mean that the lookup tables are no longer feasible. You know some user will choose 654321 as a password, and we don't want attackers to be able to search all the passwords for a hashed version of that and other common passwords.

Also, we use the Blowfish cypher algorithm because it is slower than MD-5 and the various SHA hashes. That's good: we want attackers to be faced with a slow hashing algorithm, so they can't guess many thousands of passwords each second. Brute force password guessing is prohibitively time consuming with bcrypt.

Finally, it's a principle of information security to use carefully validated encryption algorithms. It's unwise to roll your own. Assume that the cybercreeps are smarter and more highly motivated than you, and you will be more secure.

Do not use the method you describe, please.

I will provide an alternative answer to the already approved answer, with emphasis on security...

As the email is unique, I would first find the user based on the email alone, get the password from the retrieved user, and compare these using bcrypt. If the email exists, but the password doesn't match, there may be a brute force attack in place, you may want to log these failed attempts so you can lock accounts or add a captcha perhaps to slow down/prevent the attack.

How is this different

ORIGINAL SOLUTION

  1. Get the user from the email and hashed password
  2. If the credentials are right, great, the user can log in. No problems here
  3. If the user is not found, was the email or password incorrect? You can't know with this one query alone.
  4. The potential attacker is free to continue trying to access a potential account by trying different passwords on the inputted email.

ALTERNATIVE SOLUTION

  1. Get the user from the email alone
  2. If the user exists, compare the hashed user password in the DB with the hash password of the inputted credentials (using bcrypt compare).
  3. If the password is incorrect, log this in another table (USER_FAILED_LOGINS), after 3 failed attempts, add a some security measure such as a captcha.
  4. If the password is correct, great, log the user in.

You could of course, use the original solution and just do another query if the credentials were incorrect to figure out if the email exists and implement the security measures at this point. Just be wary of these types of attacks and prevent against them. Also, make sure you are using prepared statements in your SQL.

Related