This question has to do with PHP's implementation of
crypt(). For this question, the first 7 characters of the salt are not counted, so a salt '
$2a$07$a' would be said to have a length of 1, as it is only 1 character of salt and seven characters of meta-data.
When using salt strings longer than 22 characters, there is no change in the hash generated (i.e., truncation), and when using strings shorter than 21 characters the salt will automatically be padded (with '
$' characters, apparently); this is fairly straightforward. However, if given a salt 20 characters and a salt 21 characters, where the two are identical except for the final character of the 21-length salt, both hashed strings will be identical. A salt 22 characters long, which is identical to the 21 length salt except for the final character, the hash will be different again.
Example In Code:
$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"
Why is this?
Some users are noting that there is a difference in the overall string, which is true. In
salt_20, offset (28, 4) is
da$., while in
salt_21, offset (28, 4) is
da2.; however, it is important to note that the string generated includes the hash, the salt, as well as instructions to generate the salt (i.e.
$2a$07$); the part in which the difference occurs is, in fact, still the salt. The actual hash is unchanged as
Thus, this is in fact not a difference in the hash produced, but a difference in the salt used to store the hash, which is precisely the problem at hand: two salts are generating the same hash.
Rembmer: the output will be in the following format:
"$2a$##$saltsaltsaltsaltsaltsaHASHhashHASHhashHASHhashHASHhash" // ^ Hash Starts Here, offset 28,32
where ## is the log-base-2 determining the number of iterations the algorithm runs for
In the comments, it was requested that I post some additional info, as the user could not reproduce my output. Execution of the following code:
var_dump( PHP_VERSION, PHP_OS, CRYPT_SALT_LENGTH, CRYPT_STD_DES, CRYPT_EXT_DES, CRYPT_MD5, CRYPT_BLOWFISH );
Produces the following output:
string(5) "5.3.0" string(5) "WINNT" int(60) int(1) int(1) int(1) int(1)
Hope this helps.