开发者

Looking to arrive at a usable solution for password salting in a PHP/MySQL application?

开发者 https://www.devze.com 2023-02-01 11:57 出处:网络
After reading about password hashing/salting for an entire day (no lie!), I\'m in need of arriving at a solution that works, can be used consistently, and is about secure enough for a variety of diffe

After reading about password hashing/salting for an entire day (no lie!), I'm in need of arriving at a solution that works, can be used consistently, and is about secure enough for a variety of different sites/applications that are using a shared codebase.

So, here's an idea of a MySQL user table:

users { id, username, password_hash, password_salt }

..and pseudo-ish code:

$s_algo       = 'sha1';
$i_iterations = 1000;
$s_password   = 'mypw123xyuACE&.!3';
$s_salt       = hash($s_algo,uniqid(开发者_StackOverflow社区mt_rand(),true));
$s_result     = $s_password;
for ($i       = 0; $i < $i_iterations; $i++) {
    $s_result    = hash($s_algo,$s_result . $s_salt);
}

echo 'Password: ' . $s_password . "\n";
echo 'Algorithm: ' . $s_algo . "\n";
echo 'Iterations Completed: ' . $i . "\n";
echo 'Salt  : ' . $s_salt . "\n";
echo 'Result: ' . $s_result . "\n";
echo 'Length: (Salt:) ' . strlen($s_salt) . ' (Result:) ' . strlen($s_result) . "\n";

The interaction (SQL) between PHP and MySQL is taken as read, as are the bits of PHP code that actually verify the given password from user-land against the stored (salted) hash at authentication time. It's not rocket science. This is from the perspective of already doing all that stuff, but with un-salted hash-only password storage.

From my reading I suspect there could be endless debates about what $s_algo should really be (ok, probably NOT md5), and also $i_iterations. So let's just consider that they are variables within this problem scenario, which might change according to the specific context, i.e. storage limitations, server load concerns, etc.

These things aside, is this methodology for creating a per-user-salted passwords in PHP generally sound? Does the 'for' loop need to be in there at all? Is the initial salt creation code ok? Is the salt-length overkill, storage-wise (equal to the eventual hash length). Please people, pick holes (but not too many!)..

Other thoughts:

- What about hash_hmac() - is that a critical improvement over multiple hash() iterations?

- PBKDF2?


Sorry, I would've commented on the post but haven't got enough rep yet.

I'd use SHA256 for my hash algo and keep the iterations around 25. Any more than that and it's really overkill. I use a very similar solution for a framework that I've applied to half a dozen sites now. I chose to create an overly complicated random character generator, but I've used it in a lot of other places, including tokenizing financial data.

Another edit: Use a random character generator like this for your salt:

function randomChar($length) {
    $characters = array("A", "B", "C", "D", "E", "F", "G", "H", "J", "K", "M", "N", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "~", "!", "@", "#", "%", "^", "&", "(", ")", ":", "{", "[", "]", "}", "|", "<", ">", ".", ",", "/", "?", "_", "+", "-", "=");
    $charactersNumber = count($characters);
    $charactersNumber--;
    $randomLength = 0;
    while ($randomLength < $length) {
        $currentCharacter = $characters[rand(0,$charactersNumber)];
        if ($currentCharacter == $previousCharacter) {
            $currentCharacter = $characters[rand(0,$charactersNumber)];
        }
        $random .= $currentCharacter;
        $previousCharacter = $currentCharacter;
        $randomLength++;
    }
    return $random;
}

Response to iteration question: If x = hash(password + salt) and from then on x = hash(x + salt)

and 1 evaluation of x takes 10ms, then 2 would take 20 and so on. So... 25 evaluations = 250ms and 1000 = 10,000ms.

While it's not going to take 10ms for each one, even .5ms over 1000 is still half a second.

If you only accepted alphanumeric passwords, and a password was 8 characters long, each iteration would add 62^8 (if they hadn't yet found the password) more hashes because they would have to do another has for every single combination they tried.


I read an article yesterday in response to one of my questions on security here: http://chargen.matasano.com/chargen/2007/9/7/enough-with-the-rainbow-tables-what-you-need-to-know-about-s.html

It specifically says that the faster the encryption is the worst it is and it listed MD5 and SHA1 as some of the worst. Although they are microseconds apart, that converts to very long times in making a rainbow table.

I read in php manuals this: http://www.php.net/manual/en/function.hash.php#89574 where the guy ran a test of each algo and came up with speeds of each one. And based on my readings and that I use RipeMD with a 50 character salt. On the things you were asking: The thing is you are generating a random salt and then storing it in the database which seems unnecessarily redundant. I'd personally rather have one salt hidden in my php code rather then many unique salts nested in a database. Plus why are you hashing the salt?

0

精彩评论

暂无评论...
验证码 换一张
取 消