2012-08-19 33 views
12

Estoy tratando de poner en práctica lo que parece ser un enfoque de autenticación muy simple usando Sinatra y Bcrypt pero está claro que me falta algo ...Rubí Bcrypt comparación de hash

Los usuarios están asignados previamente una contraseña temporal que se almacena en texto sin formato en el db.

Me autentico contra la contraseña temporal y luego creo tanto un salt como password_hash y los escribo como cadenas en el db (mongo en este caso).

Para autenticar, obtengo la sal del db y la contraseña del usuario para comparar.

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 

El valor devuelto por Bcrypt :: Engine.hash_secret (params [: clave], password_salt) es diferente de lo que se almacena en el PP (ambos son de la clase Bcrypt :: contraseña, pero no lo hacen partido).

¿Qué me falta aquí? Muchas gracias de antemano por cualquier idea!

Marc

Respuesta

23

BCrypt::Password es una subclase de String, y overrides the == method para hacer más fácil la comprobación de contraseñas. Cuando lo haga

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

se termina de realizar el hash dos veces, por lo que no coinciden. Si comparó directamente con @user.password_hash en lugar de utilizar BCrypt::Password.new, debería ver que coinciden.

La forma más "correcta" de utilizar bcrypt-ruby para contraseñas es no usar la clase Engine, solo la clase Password. No es necesario para administrar la sal de ti mismo, bcrypt se encarga de eso y lo incluye en la cadena de hash de la contraseña:

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

puts password_salt 
puts password_hash 

produce algo como esto:

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

Usted obtendrá algo ligeramente diferente si lo ejecuta, ya que se generará una sal diferente, pero puede ver que el hash de la contraseña incluye la sal.

En su caso, usted quiere algo como esto:

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

Muchísimas gracias. Eso era exactamente lo que me estaba perdiendo, funciona perfectamente. Puedo dejar de sacarme el pelo (hasta lo siguiente). – user1553220

+1

podrías hacer @ user_hash.is_password? params [: password] en lugar de == ... Creo que es más claro y propenso a errores, porque si no sabe que == se sobrescribió e invierte el orden de comparación (params [: password] == @user_hash), devolverá falso ... – rizidoro

Cuestiones relacionadas