has_secure_password - 只有哈希或加密?

时间:2017-06-02 10:31:11

标签: ruby-on-rails ruby bcrypt-ruby

我不完全确定当您在Rails模型中添加has_secure_password时,是否涉及任何加密。我知道那里肯定有盐的哈希,但有加密吗? bcrypt可以使用blowfish,但它是否在bcrypt-ruby(所有这些背后的宝石)中使用?

1 个答案:

答案 0 :(得分:1)

TL; DR:has_secure_password会让您在使用self.password=方法时使用Bcrypt的哈希函数。

让我们看一下has_secure_password的代码:

    # File activemodel/lib/active_model/secure_password.rb, line 53
    def has_secure_password(options = {})
      # Load bcrypt gem only when has_secure_password is used.
      # This is to avoid ActiveModel (and by extension the entire framework)
      # being dependent on a binary library.
      begin
        require "bcrypt"
      rescue LoadError
        $stderr.puts "You don't have bcrypt installed in your application. Please add it to your Gemfile and run bundle install"
        raise
      end

      include InstanceMethodsOnActivation

      if options.fetch(:validations, true)
        include ActiveModel::Validations

        # This ensures the model has a password by checking whether the password_digest
        # is present, so that this works with both new and existing records. However,
        # when there is an error, the message is added to the password attribute instead
        # so that the error message will make sense to the end-user.
        validate do |record|
          record.errors.add(:password, :blank) unless record.password_digest.present?
        end

        validates_length_of :password, maximum: ActiveModel::SecurePassword::MAX_PASSWORD_LENGTH_ALLOWED
        validates_confirmation_of :password, allow_blank: true
      end
    end

我们可以看到 NOT 哈希/加密任何东西。不过,我们注意到:

      include InstanceMethodsOnActivation

如果我们继续InstanceMethodsOnActivation的文档,我们会收到以下代码:

def password=(unencrypted_password)
  if unencrypted_password.nil?
    self.password_digest = nil
  elsif !unencrypted_password.empty?
    @password = unencrypted_password
    cost = ActiveModel::SecurePassword.min_cost ? BCrypt::Engine::MIN_COST : BCrypt::Engine.cost
    self.password_digest = BCrypt::Password.create(unencrypted_password, cost: cost)
  end
end

因此,has_secure_password不加密/散列任何内容,但包括InstanceMethodsOnActivation模块。该模块定义了password=方法。这种方法的重要部分是:

self.password_digest = BCrypt::Password.create(unencrypted_password, cost: cost)

现在让我们看看BCrypt::Password.create的代码:

  def create(secret, options = {})
    cost = options[:cost] || BCrypt::Engine.cost
    raise ArgumentError if cost > 31
    Password.new(BCrypt::Engine.hash_secret(secret, BCrypt::Engine.generate_salt(cost)))
  end

  def valid_hash?(h)
    h =~ /^\$[0-9a-z]{2}\$[0-9]{2}\$[A-Za-z0-9\.\/]{53}$/
  end
end

在这种方法中,我们特别注意到:

Password.new(BCrypt::Engine.hash_secret(secret, BCrypt::Engine.generate_salt(cost)))

所以它似乎是一个哈希,这是合乎逻辑的(无论如何你都不想解密密码)。