用bcrypt发生了什么事?

时间:2014-01-13 07:07:46

标签: ruby-on-rails bcrypt

这是来自github页面:

require 'bcrypt'

class User < ActiveRecord::Base
  # users.password_hash in the database is a :string
  include BCrypt

  def password
    @password ||= Password.new(password_hash)
  end

  def password=(new_password)
    @password = Password.create(new_password)
    self.password_hash = @password
  end
end

要访问密码方法,您需要将其作为创建方法中的属性调用:

@user.password = user_params[:password]
@user.save

好......好?但现在储存的盐在哪里?我根本就没有得到它,这又如何远程安全?

要检索哈希密码,您需要以下方法:

  def password
    @password ||= Password.new(password_hash)
  end

并将其称为属性:

  if @user.password == params[:password]
    give_token
  else
    false
  end

所以看来一切都在没有盐的情况下......它是如何做到的?

这意味着我现在只需要在我的数据库中使用一列来处理密码,对吧?passwordpassword_hash而不是password_salt | password_hash

那么为什么github页面会这样说:

  

但即使这有弱点 - 攻击者可以只运行列表   可能的密码通过相同的算法,将结果存储在一个   大数据库,然后通过哈希查找密码:

PrecomputedPassword.find_by_hash(<unique gibberish>).password #=> "secret1"
Salts

然后这就是我真正得到的:

The solution to this is to add a small chunk of random data -- called a salt -- to the password before it's hashed:

如果bcrypt自动处理所有内容,他们为什么要解释所有这些?

hash(salt + p) #=> <really unique gibberish>
The salt is then stored along with the hash in the database, and used to check potentially valid passwords:

<really unique gibberish> =? hash(salt + just_entered_password)
bcrypt-ruby automatically handles the storage and generation of these salts for you.

有人可以解释bcrypt如何存储和生成这些盐吗?为什么它说它为我处理这一切然后继续告诉我如何生成盐?我是否需要在我的模型中运行类似的内容:self.password_hash = hash(salt + p)

很困惑我曾经完全得到盐和哈希,现在他们已经改变了一切。 糟糕,不清楚的文档 ......他们似乎向您展示了如何在没有盐的情况下使用bcrypt和大量示例,然后简要提及如何正确地使用盐在底部。

请问有人请举例说明如何使用新版本的bcrypt生成salt和hash,以及如何进行身份验证?

2 个答案:

答案 0 :(得分:12)

好的,has_secure_password非常酷。您不再需要担心盐和哈希值,盐和哈希值将作为一个属性(password_digest)存储在数据库中。

它以这样的方式保存,即bcrypt知道password_digest字符串的哪一部分是盐,以及什么是哈希。

如果您从头开始设置身份验证,则需要执行以下操作:

1)添加bcrypt rails gem:

gem bcrypt-rails

2)将has_secure_password方法添加到负责处理用户记录的模型中:

class User < ActiveRecord::Base
    has_secure_password
end

3)确保您的用户表格中包含password_digest列:

class CreateUser < ActiveRecord::Migration
    create_table :users do |t|
        t.username
        t.password_digest
    end
end

4)创建一个new方法,为要使用的表单创建一个新的空用户实例:

class UsersController < ApplicationController
    def new
        @user = User.new
    end
end

5)在new视图中,创建一个表单,用于填充params散列':password:username条目:

<% form_for( @user ) do |f| %>
    <%= f.text_field :username %>
    <%= f.password_field :password %>
<% end %>

6)返回我们的控制器,使用强参数允许用户名和密码。强大的params背后的全部原因是为了防止一些厚颜无耻的chappy使用开发工具创建自己的html表单字段(例如与id相关的字段)并用恶意数据填充数据库:

class UsersController < ApplicationController
    def new
        @user = User.new
    end

    private

    def user_params
           params.require(:user).permit(:username, :password)
    end
end

7)让我们创建一个create方法,该方法将使用这些允许的条目来创建一个新用户,由表单填充:

class UsersController < ApplicationController
    def new
        @user = User.new
    end

    def create
            @user = User.new(user_params)
            @user.save
            redirect_to root_path
    end

    private

    def user_params
           params.require(:user).permit(:username, :password)
    end
end

根据您的需要设置路线,就是这样!用户记录的password_digest列将自动填充一个字符串,该字符串包含附加密码哈希值的salt。十分优雅。

您需要记住的所有内容:password - &gt; password_digest

为了授权用户并注销用户,使用create方法和destroy方法创建会话控制器:

def create
  user = User.find_by_email(params[:email])
  if user && user.authenticate(params[:password])
    session[:user_id] = user.id
    redirect_to admin_root_path, :notice => "Welcome back, #{user.username}"
  else
    flash.now.alert = "Invalid email or password"
    redirect_to root_path
  end
end

def destroy
  reset_session
  flash[:info] = "Signed out successfully!"
  redirect_to root_path
end

希望这有助于某人!

答案 1 :(得分:3)

bcrypt为你准备了一切。您的密码摘要包含几种类型的信息,bcrypt算法类型,成本,盐和校验和。

例如:

my_password = BCrypt::Password.create("my password")
#=> "$2a$10$.kyRS8M3OICtvjBpdDd1seUtlvPKO5CmYz1VM49JL7cJWZDaoYWT."

第一部分:$2a$是算法的变体,请参阅:Where 2x prefix are used in BCrypt?

第二部分10是成本参数,您可以通过提供哈希{cost: 12}作为create的第二个参数来增加它以减慢进程(对数值)。 / p>

现在,如果您致电my_password.salt,则会获得"$2a$10$.kyRS8M3OICtvjBpdDd1se",其中标识了用作创建校验和的关键部分。

最后,您的校验和为"UtlvPKO5CmYz1VM49JL7cJWZDaoYWT."。这就是为什么你第二次调用create时字符串不同,因为将使用另一个盐。

但正如我之前提到的,你不需要做任何额外的事情,因为所有这些都是为你照顾的。