has_secure_password - 只散列还是加密?
has_secure_password - only hashes or encrypts as well?
我不完全确定,当您在 Rails 模型中添加 has_secure_password
时,是否涉及任何加密。我知道肯定有加盐的散列,但是有加密吗? bcrypt 可以使用河豚,但它是否被用于 bcrypt-ruby
(所有这些背后的 gem)?
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
我们可以看到它没有 hash/encrypt 任何作用。然而,我们注意到:
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
没有 encrypt/hash 任何内容,但包含 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)))
所以它似乎是一个哈希,这是合乎逻辑的(无论如何你都不想解密密码)。
我不完全确定,当您在 Rails 模型中添加 has_secure_password
时,是否涉及任何加密。我知道肯定有加盐的散列,但是有加密吗? bcrypt 可以使用河豚,但它是否被用于 bcrypt-ruby
(所有这些背后的 gem)?
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
我们可以看到它没有 hash/encrypt 任何作用。然而,我们注意到:
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
没有 encrypt/hash 任何内容,但包含 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)))
所以它似乎是一个哈希,这是合乎逻辑的(无论如何你都不想解密密码)。