Подтвердить что ты не робот

Сравнение с хэшем Ruby BCrypt

Я пытаюсь реализовать то, что кажется очень простым методом аутентификации с использованием Sinatra и BCrypt, но, очевидно, я что-то пропустил...

Пользователи предварительно назначают временный пароль, который хранится в открытом тексте в db.

Я аутентифицируюсь на временном пароле, а затем создаю как salt, так и пароль_hash и записываю их как строки в db (в этом случае mongo).

Для проверки подлинности я извлекаю соль из db и пароль пользователя для сравнения.

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

Значение, возвращаемое BCrypt:: Engine.hash_secret (params [: password], password_salt) отличается от того, что хранится в db (оба имеют класс BCrypt:: Password, но они не совпадают).

Что мне здесь не хватает? Большое спасибо заранее за любую проницательность!

Марк

4b9b3361

Ответ 1

BCrypt::Password является подклассом String, и переопределяет метод ==, чтобы упростить проверку паролей. Когда вы делаете

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

вы в конечном итоге выполняете хэш дважды, и поэтому они не совпадают. Если вы сравнили напрямую с @user.password_hash, а не с помощью BCrypt::Password.new, вы должны увидеть, что они соответствуют.

Более "правильный" способ использования bcrypt-ruby для паролей - не использовать класс Engine вообще, просто класс Password. Вам не нужно самостоятельно управлять солью, bcrypt заботится об этом и включает его в строку хэша паролей:

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

puts password_salt
puts password_hash

создает что-то вроде этого:

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

У вас получится немного другое, если вы запустите его, так как будет сформирована другая соль, но вы можете видеть, что хеш пароля включает соль.

В вашем случае вы хотите что-то вроде этого:

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