Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ruby BCrypt hash comparison

Tags:

ruby

bcrypt

I'm trying to implement what seems like a very simple authentication approach using Sinatra and BCrypt but clearly I'm missing something...

Users are preassigned a temporary password which is stored in plaintext in the db.

I authenticate against the temp password and then create both a salt and password_hash and write them as strings to the db (mongo in this case).

To authenticate I fetch the salt from the db and user password to compare.

post "/password_reset" do
  user = User.first(:email => params[:email], :temp_password => params[:temp_password])
  if dealer != nil then
  password_salt = BCrypt::Engine.generate_salt
  password_hash = BCrypt::Engine.hash_secret(params[:password], password_salt)
  user.set(:password_hash => password_hash)
  user.set(:password_salt => password_salt)
  end
end

post "/auth" do
  @user = User.first(:email => params[:email])
  @user_hash = BCrypt::Password.new(@user.password_hash) #because the password_hash is  stored in the db as a string, I cast it as a BCrypt::Password for comparison
  if @user_hash == BCrypt::Engine.hash_secret(params[:password], @user.password_salt.to_s)   then
    auth = true
  else
    auth = false
  end
end

The value returned by BCrypt::Engine.hash_secret(params[:password], password_salt) is different than what is stored in the db (both are of class BCrypt::Password, but they don't match).

What am I missing here? Many thanks in advance for any insight!

Marc

like image 353
user1553220 Avatar asked Aug 19 '12 18:08

user1553220


People also ask

Is bcrypt better than SHA256?

The technology in the Bcrypt algorithm and process limits attacks and makes it harder for attackers to compromise passwords. Bcrypt was not designed for encrypting large amounts of data. It is best implemented for passwords, however SHA-256 is better for large amounts of data because it is less costly and faster.

Is bcrypt hash unique?

A BCrypt hash includes salt and as a result this algorithm returns different hashes for the same input.

Can bcrypt be hacked?

Data security systems that rely on MD5 can be easily hacked by anyone with a basic understanding of the function. On the other hand, bcrypt is not broken. As a result, it's still able to keep passwords and information safe.

Which is more secure bcrypt or Scrypt?

Comparison to other password hashing algorithms Password hashing generally needs to complete < 1000 ms. In this scenario, bcrypt is stronger than pbkdf2, scrypt, and argon2.


1 Answers

BCrypt::Password is a subclass of String, and it overrides the == method to make checking passwords easier. When you do

if @user_hash == BCrypt::Engine.hash_secret(params[:password], @user.password_salt.to_s)

you end up performing the hash twice, and so they don’t match. If you compared directly with @user.password_hash rather than using BCrypt::Password.new you should see that they match.

The more “correct” way to use bcrypt-ruby for passwords is to not use the Engine class at all, just the Password class. You don’t need to manage the salt yourself, bcrypt takes care of that and includes it in the password hash string:

password_salt = BCrypt::Engine.generate_salt
password_hash = BCrypt::Engine.hash_secret("s3kr1t!", password_salt)

puts password_salt
puts password_hash

produces something like this:

$2a$10$4H0VpZjyQO9SoAGdfEB5j.
$2a$10$4H0VpZjyQO9SoAGdfEB5j.oanIOc4zp3jsdTra02SkdmhAVpGK8Z6

You’ll get something slightly different if you run it, since a different salt will be generated, but you can see that the password hash includes the salt.

In your case, you want something like this:

post "/password_reset" do
  user = User.first(:email => params[:email], :temp_password => params[:temp_password])
  if dealer != nil then
    password_hash = BCrypt::Password.create(params[:password])
    user.set(:password_hash => password_hash) # no need to store the salt separately in the database
  end
end

post "/auth" do
  @user = User.first(:email => params[:email])
  @user_hash = BCrypt::Password.new(@user.password_hash)
  if @user_hash == params[:password]  then # overridden == method performs hashing for us
    auth = true
  else
    auth = false
  end
end
like image 113
matt Avatar answered Oct 05 '22 12:10

matt