Domanda Confronto di hash rubino BCrypt


Sto cercando di implementare quello che sembra un approccio di autenticazione molto semplice usando Sinatra e BCrypt ma chiaramente mi manca qualcosa ...

Gli utenti sono preassegnati una password temporanea che viene archiviata in chiaro nel db.

Autenticherò contro la password temporanea e quindi creerò sia un comando salt che password_hash e li scriverò come stringhe sul db (mongo in questo caso).

Per autenticare, prelevo il sale dalla password db e utente per confrontare.

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

Il valore restituito da BCrypt :: Engine.hash_secret (params [: password], password_salt) è diverso da quello memorizzato nel db (entrambi sono di classe BCrypt :: Password, ma non corrispondono).

Cosa mi manca qui? Molte grazie in anticipo per qualsiasi intuizione!

Marc


12
2017-08-19 18:27


origine


risposte:


BCrypt::Password è una sottoclasse di String, ed esso sovrascrive il == metodo per semplificare il controllo delle password. Quando lo fai

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

finisci per eseguire l'hash due voltee quindi non corrispondono. Se confrontato direttamente con @user.password_hash piuttosto che usare BCrypt::Password.new dovresti vedere che corrispondono.

Il modo più "corretto" di usare bcrypt-ruby per le password è di non usare il Engine classe a tutti, solo il Password classe. Non è necessario gestire il sale da soli, bcrypt si occupa di questo e lo include nella stringa hash della password:

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

puts password_salt
puts password_hash

produce qualcosa come questo:

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

Otterrai qualcosa di leggermente diverso se lo esegui, poiché verrà generato un sale diverso, ma puoi vedere che l'hash della password include il sale.

Nel tuo caso, vuoi qualcosa del genere:

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

24
2017-08-19 19:29