Ruby on Rails:当我在admin属性上使用“toggle”方法时,为什么用户的加密密码会在数据库中被更改?

我刚刚完成了Hartl的Rails教程书,我在我的第一个rails应用程序中使用了他的帐户认证逻辑 。 但是,当我创建一个新用户帐户并通过在控制台中切换admin属性(例如User.find(5).toggle!(:admin))将其设置为管理员帐户时,存储在DB中的加密密码得到修改。 为什么?

这是用户模型逻辑……

class User  true, :length => { :maximum => 50 } validates :email, :presence => true, :format => { :with => email_regex }, :uniqueness => true validates :password, :presence => true, :confirmation => true, :length => { :within => 6..40 } #class method that authenticates a user, used to create a session cookie def self.authenticate(email, submitted_password) user = find_by_email(email) return nil if user.nil? return user if user.has_password?(submitted_password) end #used to authenticate a signed user from a signed cookie def self.authenticate_with_salt(id, cookie_salt) user = find_by_id(id) return nil if user.nil? return user if user.salt == cookie_salt end #callback that occurs before a record is successfully saved (meaning it has a valud password) before_save :encrypt_password def has_password?(submitted_password) encrypted_password == encrypt(submitted_password) end private #self keyword is required when assigning to a instance attribute def encrypt_password self.salt = make_salt if new_record? self.encrypted_password = encrypt(password) end def encrypt(string) secure_hash("#{salt}--#{string}") end def make_salt secure_hash("#{Time.now.utc}--#{password}") end def secure_hash(string) Digest::SHA2.hexdigest(string) end end 

这就是行为的样子……

 ruby-1.9.2-p180 :018 > User.last => # ruby-1.9.2-p180 :019 > User.last.toggle!(:admin) => true ruby-1.9.2-p180 :020 > User.last => # 

非常感谢你的帮助!

尝试更改您的before_save方法:

 def encrypt_password if password.present? self.salt = make_salt if new_record? self.encrypted_password = encrypt(password) end end 

UPD 。 或者你可以把它缩短一点

 def encrypt_password self.salt = make_salt if new_record? self.encrypted_password = encrypt(password) if password.present? end 

不确定具体是什么。 可能是密码使用不同的盐(基于时间)再次加密。 尝试将debugger添加到encrypt_password的第一行,然后从控制台运行相同的代码,以查看在运行toggle!时密码是否加密toggle!

你的代码中有一些奇怪的东西。 盐应该与密码无关,但是你的(Hartl’s?)make_salt方法说:

 def make_salt secure_hash("#{Time.now.utc}--#{password}") end 

可能是你的nil问题的根源,因为你在make_salt中访问password ; 在任何情况下,这都是糟糕的加密,因为它相当于使用Time.now作为“随机”盐,这容易破解(构建彩虹表)。

您应该使用一个好的随机数生成器,例如Ruby的内置SecureRandom

 def make_salt SecureRandom.hex(64) end 

为什么这么长的盐? 根据https://crackstation.net/hashing-security.htm ,“为了让攻击者无法为每种可能的盐创建查找表,盐必须很长。一个好的经验法则是使用盐这与散列函数的输出大小相同。例如,SHA256的输出是256位(32字节),因此salt应至少为32个随机字节。“ 我不想使用SecureRandom.random_bytes(32)来避免使用非ascii字符的潜在数据库字符串编码问题,并且64个随机hex字符包含32个随机字节,我认为它们被视为相同的熵。