2012-08-19 5 views
12

Я пытаюсь реализовать то, что кажется очень простой подход аутентификации с использованием Синатра и Bcrypt, но ясно, что я что-то отсутствует ...рубин BCrypt сравнение хэш

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

Я аутентифицируюсь от временного пароля, а затем создаю как соль, так и пароль_hash и записываю их как строки в 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_salt) отличается от того, что хранится в БД (оба класса Bcrypt :: Пароль, но они этого не делают совпадение).

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

Марк

ответ

23

BCrypt::Password является подклассом String, и overrides the == method сделать проверки паролей проще. Когда вы делаете

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 
+1

благодарю вас так много. Это было именно то, чего я отсутствовал, прекрасно работает. Я могу перестать вытягивать волосы (до следующей вещи). – user1553220

+1

вы могли бы сделать @ user_hash.is_password? params [: password] вместо == ... Я думаю, что это более понятно и подвержено ошибкам, потому что, если вы не знаете, что == был перезаписан и инвертирован порядок сравнения (params [: password] == @user_hash) он вернет false ... – rizidoro

Смежные вопросы