2012-08-19 13 views
12

Ich versuche, sehr einfache Authentifizierung Ansatz zu implementieren, was wie eine mit Sinatra und BCrypt scheint aber mir fehlt etwas klar ...Rubin BCrypt Hash-Vergleich

Benutzer ein temporäres Passwort vorbelegt werden, die im Nur-Text gespeichert wird in der db.

Ich authentifizieren gegen das temp-Passwort und dann erstellen Sie eine Salz-und password_hash und schreiben Sie sie als Zeichenfolgen für die db (Mongo in diesem Fall).

Um authentifizieren Ich holen Sie das Salz aus dem DB und Benutzer-Passwort zu vergleichen.

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 

Der zurückgegebene Wert von BCrypt :: Engine.hash_secret (params [: Kennwort], password_salt) ist anders als das, was in der DB gespeichert ist (beide sind von Klasse BCrypt :: Passwort, aber sie tun nicht Spiel).

Was fehlt mir hier? Vielen Dank im Voraus für jede Einsicht!

Marc

Antwort

23

BCrypt::Password ist eine Unterklasse von String, und es overrides the == method zu machen Überprüfung Passwörter einfacher. Wenn Sie

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

tun beenden Sie den Hash-Bühne bis zweimal, und so passen sie nicht. Wenn Sie direkt mit @user.password_hash verglichen anstatt BCrypt::Password.new verwenden, sollten Sie sehen, dass sie übereinstimmen.

Die "korrekere" Art, bcrypt-ruby für Passwörter zu verwenden, ist, die Engine Klasse überhaupt nicht zu verwenden, nur die Password Klasse. Sie brauchen nicht das Salz selbst zu verwalten, nimmt bcrypt Sorge, dass und schließt sie in der Passwort-Hash-String:

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

puts password_salt 
puts password_hash 

etwas wie folgt erzeugt:

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

Sie werden etwas leicht bekommen anders, wenn Sie es ausführen, da ein anderes Salz generiert wird, aber Sie können sehen, dass der Passwort-Hash das Salz enthält.

In Ihrem Fall sollten Sie etwas wie folgt aus:

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

DANKE so viel. Genau das habe ich vermisst, funktioniert perfekt. Ich kann aufhören, meine Haare auszuziehen (bis zum nächsten). – user1553220

+1

könnten Sie @ user_hash.is_password tun? params [: password] statt == ... Ich denke, es ist klarer und fehleranfällig, denn wenn Sie nicht wissen, dass == überschrieben wurde und die Vergleichsreihenfolge invertiert (params [: password] == @user_hash), es wird falsch zurückgeben ... – rizidoro