12

Sinatra と BCrypt を使用して非常に単純な認証アプローチのように見えるものを実装しようとしていますが、明らかに何かが欠けています...

ユーザーには、db にプレーンテキストで保存される一時パスワードが事前に割り当てられます。

一時パスワードに対して認証を行ってから、salt と password_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) によって返される値は、データベースに格納されている値とは異なります (どちらも BCrypt::Password クラスですが、一致しません)。

ここで何が欠けていますか?洞察力について事前に感謝します!

マルク

4

1 に答える 1

24

BCrypt::Passwordは のサブクラスでありStringメソッドをオーバーライドして==パスワードのチェックを容易にします。あなたがするとき

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

ハッシュを 2 回実行することになるため、それらは一致しません。@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
于 2012-08-19T19:29:28.283 に答える