After discussing this on the security list and in IRC, I am opening this issue report for discussion.
The password hashing function does use salted and iterated hashing
which is far better than not hashing.
However, it does not abide by industry standard stretching practices.
<?php
// We rely on the hash() function being available in PHP 5.2+.
$hash = hash($algo, $salt . $password, TRUE);
do {
$hash = hash($algo, $hash . $password, TRUE);
} while (--$count);
?>
There are a few problems with a function such as this:
First, if there is ever a collision for $hash.$password, that
collision will propegate through to the result. This means that
increasing iteration counts can actually decrease the output space of
the function. This can theoretically lead to password collisions
(where multiple password inputs combinations lead to the same output
hash string).
Second, straight concatenation in the message of the hash function can
be susceptible to certain padding attacks. While the iterations make
the vector more difficult to execute, it's still theoretically
possible.
Suggestion:
Implement the industry standard PBKDF2 derivation function.
http://www.ietf.org/rfc/rfc2898.txt
A PHP implementation looks like this:
https://github.com/ircmaxell/PHP-CryptLib/blob/master/lib/CryptLib/Key/D...
<?php
public function derive($algorithm, $password, $salt, $iterations, $length) {
$size = strlen(hash($algorithm, '', true));
$len = ceil($length / $size);
$result = '';
for ($i = 1; $i <= $len; $i++) {
$tmp = hash_hmac($algorithm, $salt . pack('N', $i),
$password, true);
$res = $tmp;
for ($j = 1; $j < $iterations; $j++) {
$tmp = hash_hmac($algorithm, $tmp, $password, true);
$res ^= $tmp;
}
$result .= $res;
}
return substr($result, 0, $length);
}
?>
(Note, I tweaked it to be pure PHP rather than dependent upon the
CryptLib library).
This implementation is tested against the test
vectors for PBKDF2:
https://github.com/ircmaxell/PHP-CryptLib/blob/master/test/Unit/Key/Deri...