为何传入BCrypt哈希作为盐时,String.crypt能生成匹配的哈希?
问题:为何同一方法能同时验证String.crypt和BCrypt哈希?
我发现部分代码使用String.crypt处理密码哈希,于是尝试寻找可同时验证BCrypt哈希与旧版String.crypt哈希的方法。意外发现以下方法对两种哈希均能正常工作,但不清楚其原理:
def check_password(password, stored_hash) password.crypt(stored_hash) == stored_hash end
测试情况如下:
string_crypt_pass = "mysuperpass".crypt("$1$abasasa") bcrypt_pass = BCrypt::Password.create("mysuperpass").to_s check_password("mysuperpass", string_crypt_pass) #true check_password("mywrongpass", string_crypt_pass) #false check_password("mysuperpass", bcrypt_pass) #true check_password("mywrongpass", bcrypt_pass) #false
请问该方法为何能生效?
解答
这个方法能生效的核心原因是Ruby的String#crypt方法本身具备自动适配哈希算法的能力,具体逻辑分两种情况:
1. 对String.crypt生成的哈希的处理
String#crypt生成的哈希格式自带算法标识,比如例子里的$1$abasasa$...:
$1$是MD5算法的标识(crypt支持多种哈希算法,通过前缀区分)- 紧随其后的
abasasa是盐值 - 最后一段是密码的哈希结果
当调用password.crypt(stored_hash)时,方法会自动从传入的stored_hash中解析出算法标识+盐值(也就是$1$abasasa),然后用对应的MD5算法重新计算输入密码的哈希。如果密码正确,重新计算的结果会和原stored_hash完全一致,比对返回true;密码错误则结果不同,返回false。
2. 对BCrypt生成的哈希的处理
BCrypt生成的哈希格式是$2a$xx$...(xx是成本因子,比如10):
$2a$是BCrypt算法的标识- 主流系统的
crypt库都支持BCrypt算法,Ruby的String#crypt会识别这个标识 - 方法会从
stored_hash中提取出成本因子和盐值,用BCrypt算法重新计算输入密码的哈希
同样,密码正确时重新计算的哈希会和原BCrypt哈希完全匹配,比对成立;密码错误则不匹配。
简单来说,String#crypt的设计逻辑就是:传入已有的哈希作为参数时,它会自动复用该哈希里的算法和盐信息,重新计算密码哈希,所以不管是旧版crypt生成的哈希还是BCrypt哈希,都能通过这种方式完成验证。
内容的提问来源于stack exchange,提问作者hat
相关产品推荐
相关产品推荐

