这个问题与PHP的实现有关crypt()。对于此问题,不计算盐的前7个字符,因此盐’ $2a$07$a‘的长度为1,因为它仅是盐的1个字符和元数据的7个字符。
crypt()
$2a$07$a
当使用长度超过22个字符的盐字符串时,生成的哈希值不会发生变化(即,截断),而当使用长度小于21个字符的字符串时,盐将被自动填充($显然带有’ ‘字符);这非常简单。但是,如果给定一个Salt 20个字符和Salt 21个字符,并且除了21个长度的salt的最后一个字符外,这两个字符相同,则两个哈希字符串都是相同的。一个长22个字符的盐,除了最后一个字符外,它与长21个盐的盐相同,哈希值将再次不同。
$
代码示例:
$foo = 'bar'; $salt_xx = '$2a$07$'; $salt_19 = $salt_xx . 'b1b2ee48991281a439d'; $salt_20 = $salt_19 . 'a'; $salt_21 = $salt_20 . '2'; $salt_22 = $salt_21 . 'b'; var_dump( crypt($foo, $salt_19), crypt($foo, $salt_20), crypt($foo, $salt_21), crypt($foo, $salt_22) );
将产生:
string(60) "$2a$07$b1b2ee48991281a439d$$.dEUdhUoQXVqUieLTCp0cFVolhFcbuNi" string(60) "$2a$07$b1b2ee48991281a439da$.UxGYN739wLkV5PGoR1XA4EvNVPjwylG" string(60) "$2a$07$b1b2ee48991281a439da2.UxGYN739wLkV5PGoR1XA4EvNVPjwylG" string(60) "$2a$07$b1b2ee48991281a439da2O4AH0.y/AsOuzMpI.f4sBs8E2hQjPUQq"
为什么是这样?
一些用户注意到整个字符串存在差异,这是事实。在中salt_20,偏移量(28,4)为da$.;而在中salt_21,偏移量(28,4)为da2.; 但是,必须注意,所生成的字符串包括哈希,盐以及生成盐的指令(即$2a$07$);实际上,发生差异的部分仍然是盐。实际的哈希值不变为UxGYN739wLkV5PGoR1XA4EvNVPjwylG。
salt_20
da$.
salt_21
da2.
$2a$07$
UxGYN739wLkV5PGoR1XA4EvNVPjwylG
因此,实际上这并不是所产生的哈希值的差异,而是用于存储哈希值的盐的差异,而这恰恰是当前的问题:两种盐正在生成相同的哈希值。
Rembmer:输出将采用以下格式:
"$2a$##$saltsaltsaltsaltsaltsaHASHhashHASHhashHASHhashHASHhash" // ^ Hash Starts Here, offset 28,32
其中##是对数为2的对数,确定算法运行的迭代次数
在评论中,要求我发布一些附加信息,因为用户无法复制我的输出。执行以下代码:
var_dump( PHP_VERSION, PHP_OS, CRYPT_SALT_LENGTH, CRYPT_STD_DES, CRYPT_EXT_DES, CRYPT_MD5, CRYPT_BLOWFISH );
产生以下输出:
string(5) "5.3.0" string(5) "WINNT" int(60) int(1) int(1) int(1) int(1)
希望这可以帮助。
经过一些实验,我得出的结论是,这是由于盐的处理方式所致。盐不被认为是文字文本,而是被base64编码的字符串,这样22字节的salt数据实际上将代表16字节floor(22 * 24 / 32) == 16的salt 字符串()。“ Gotcha!” 但是,这种实现方式与Unix crypt一样,使用的是“非标准” base64字母。确切地说,它使用以下字母:
floor(22 * 24 / 32) == 16
./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789$
第65个字符“ $”是填充字符。
现在,该crypt()函数似乎能够提取小于或等于其最大值的任何长度的盐,并通过丢弃不组成另一个完整字节的任何数据来静默处理base64中的任何不一致之处。如果您在盐中传递不属于base64字母的字符,则crypt函数将完全失败,这恰恰证实了这一操作原理。
取一个假想的盐’ 1234‘。这是与base64完全一致的,因为它表示24位数据,即3个字节,并且不携带任何需要丢弃的数据。这Len Mod 4是零的盐。在该盐上附加任何字符,它将变成5个字符的盐,Len Mod 4现在为1。但是,此附加字符仅表示六位数据,因此不能转换为另一个完整字节,因此将其丢弃。
1234
Len Mod 4
因此,对于任何两种盐A和B,其中
Len A Mod 4 == 0 && Len B Mod 4 == 1 // these two lines mean the same thing && Len B = Len A + 1 // but are semantically important separately && A == substr B, 0, Len A
实际上,用于crypt()计算哈希的实际盐将是相同的。为了证明这一点,我提供了一些示例PHP代码,可以用来说明这一点。盐以 半 非随机的方式不断旋转(基于当前时间的漩涡哈希的随机段至微秒),要哈希的数据(此处称为$seed)只是当前的Unix-Epoch时间。
$seed
$salt = substr(hash('whirlpool',microtime()),rand(0,105),22); $seed = time(); for ($i = 0, $j = strlen($salt); $i <= $j; ++$i) { printf('%02d = %s%s%c', $i, crypt($seed,'$2a$07$' . substr($salt, 0, $i)), $i%4 == 0 || $i % 4 == 1 ? ' <-' : '', 0x0A ); }
这会产生类似于以下内容的输出
00 = $2a$07$$$$$$$$$$$$$$$$$$$$$$.rBxL4x0LvuUp8rhGfnEKSOevBKB5V2. <- 01 = $2a$07$e$$$$$$$$$$$$$$$$$$$$.rBxL4x0LvuUp8rhGfnEKSOevBKB5V2. <- 02 = $2a$07$e8$$$$$$$$$$$$$$$$$$$.WEimjvvOvQ.lGh/V6HFkts7Rq5rpXZG 03 = $2a$07$e89$$$$$$$$$$$$$$$$$$.Ww5p352lsfQCWarRIWWGGbKa074K4/. 04 = $2a$07$e895$$$$$$$$$$$$$$$$$.ZGSPawtL.pOeNI74nhhnHowYrJBrLuW <- 05 = $2a$07$e8955$$$$$$$$$$$$$$$$.ZGSPawtL.pOeNI74nhhnHowYrJBrLuW <- 06 = $2a$07$e8955b$$$$$$$$$$$$$$$.2UumGVfyc4SgAZBs5P6IKlUYma7sxqa 07 = $2a$07$e8955be$$$$$$$$$$$$$$.gb6deOAckxHP/WIZOGPZ6/P3oUSQkPm 08 = $2a$07$e8955be6$$$$$$$$$$$$$.5gox0YOqQMfF6FBU9weAz5RmcIKZoki <- 09 = $2a$07$e8955be61$$$$$$$$$$$$.5gox0YOqQMfF6FBU9weAz5RmcIKZoki <- 10 = $2a$07$e8955be616$$$$$$$$$$$.hWHhdkS9Z3m7/PMKn1Ko7Qf2S7H4ttK 11 = $2a$07$e8955be6162$$$$$$$$$$.meHPOa25CYG2G8JrbC8dPQuWf9yw0Iy 12 = $2a$07$e8955be61624$$$$$$$$$.vcp/UGtAwLJWvtKTndM7w1/30NuYdYa <- 13 = $2a$07$e8955be616246$$$$$$$$.vcp/UGtAwLJWvtKTndM7w1/30NuYdYa <- 14 = $2a$07$e8955be6162468$$$$$$$.OTzcPMwrtXxx6YHKtaX0mypWvqJK5Ye 15 = $2a$07$e8955be6162468d$$$$$$.pDcOFp68WnHqU8tZJxuf2V0nqUqwc0W 16 = $2a$07$e8955be6162468de$$$$$.YDv5tkOeXkOECJmjl1R8zXVRMlU0rJi <- 17 = $2a$07$e8955be6162468deb$$$$.YDv5tkOeXkOECJmjl1R8zXVRMlU0rJi <- 18 = $2a$07$e8955be6162468deb0$$$.aNZIHogUlCn8H7W3naR50pzEsQgnakq 19 = $2a$07$e8955be6162468deb0d$$.ytfAwRL.czZr/K3hGPmbgJlheoZUyL2 20 = $2a$07$e8955be6162468deb0da$.0xhS8VgxJOn4skeI02VNI6jI6324EPe <- 21 = $2a$07$e8955be6162468deb0da3.0xhS8VgxJOn4skeI02VNI6jI6324EPe <- 22 = $2a$07$e8955be6162468deb0da3ucYVpET7X/5YddEeJxVqqUIxs3COrdym
结论?双重。首先,它按预期工作,其次,知道您自己的盐或不撒盐。