phpsecurityhashmd5sha512

Will multiple hashing passes make storing passwords safer?


I am new to PHP programming and PHP security issues. Is it safe to hash multiple times a password? Others over the internet said that it brings collisions or it was not safe at all, so my question is can a hashing method be secure if implemented like this. Ihave tried multiple things and combinations and results aren't the same, but does it really make the password more secure?

$input_pass = 'example';
$step1 = md5($input_pass);
$final_pass = hash('sha512',crypt(pi(),hash('sha512',$input_pass)));

Solution

  • So, to demonstrate why specifically this is a spectacularly bad idea (to put it lightly), let's examine the code that you have:

    $input_pass = 'example';
    
    $step1 = md5($input_pass);
    

    Ok, so you generate an MD5, and then completely ignore it...

    $t1 = hash('sha512',$input_pass);
    

    Now you hash the password with SHA512

    $t2 = crypt(pi(),$t1);
    

    And then run crypt over the output of pi() (as the input for the password field), passing in the SHA512 hash as the "salt" (more on that in a second)

    $final_pass = hash('sha512',$t2);
    

    And then hash the entire result a second time...

    Now, to see why this is so bad, let's look at the outputs of each step:

    $t1 = string(128) "3bb12eda3c298db5de25597f54d924f2e17e78a26ad8953ed8218ee682f0bbbe9021e2f3009d152c911bf1f25ec683a902714166767afbd8e5bd0fb0124ecb8a" 
    $t2 = string(13) "3b5PQJpjs2VBk" 
    $final_pass = string(128) "ec993177685eb6f2aa687d1202f47f7c5c0e17954fe1409115ed8b2170839029a065a189a3d2af6fe8d05869f7a6980743c199d7eb9d00c7e036af790231549a"
    

    Hmm, wait a second, I wonder something. Let's try changing the password to something else. Say foobar:

    $t1 = string(128) "0a50261ebd1a390fed2bf326f2673c145582a6342d523204973d0219337f81616a8069b012587cf5635f6925f1b56c360230c19b273500ee013e030601bf2425"
    $t2 = string(13) "0aTQxuCXvbnbY"
    $final_pass = string(128) "6cd37aeccd93e17667563fadfae96d50427b5187cffb1c2865ee4bcce76d6c767f2b9b6c542988fd5559efb499d988b204e49b8ed60428db45e2ccb3945f33f2"
    

    Hmmm, interesting. The first 2 characters of $t2 match the first 2 characters of $t1... I wonder if we can use that to exploit this code. Let's build a little brute-forcer to find a random password that collides with the first 2 characters of that SHA512:

    $target = '3b';
    
    $runs = 0;
    
    do {
        $runs++;
        $pass = genRandomPass();
        if ('3b' == substr(hash('sha512', $pass), 0, 2)) {
            echo "Found match: $pass\n In $runs Runs\n";
            die();
        }
    } while (true);
    
    function genRandomPass() {
        $length = mt_rand(8, 12);
        $chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
        $charLen = strlen($chars);
        $result = '';
        for ($i = 0; $i < $length; $i++) {
            $result .= $chars[mt_rand(0, $charLen - 1)];
        }
        return $result;
    }
    

    And running it:

    ~$ time php test.php 
    Found match: olhUhIWp5Xd
     In 49 Runs
    
    real    0m0.013s
    user    0m0.004s
    sys     0m0.012s
    

    Woah! In 0.013 seconds, it picked a random collision!!! Let's try it out:

    $t1 = string(128) "3ba21ea28adb4543755bf62133eb0337569170c90ae4f3eaca9b777bf88f3a2eb9f9d0e40e4ff9e8844814ac7944ccf61e2222c184ebbf91e43fcdc227c80416"
    $t2 = string(13) "3b5PQJpjs2VBk"
    $final_pass = string(128) "ec993177685eb6f2aa687d1202f47f7c5c0e17954fe1409115ed8b2170839029a065a189a3d2af6fe8d05869f7a6980743c199d7eb9d00c7e036af790231549a"
    

    Yup! In 0.013 seconds of CPU time, I just found a collision for that hash method.

    WHY

    Your hash will be limited by the narrowest component. You're incorrectly feeding the password into the salt component of crypt() when using CRYPT_DES (which is horrifically weak).

    So that means that all of the entropy of the password is being put into 2 characters. 2 characters that have 64 combinations each. So the total possible entropy of your end hash is 4096 possibilities, or 12 bits.

    Compare that to bcrypt which provides 576 bits of entropy, and you can see why it's bad...

    Conclusion

    As I've said before: Face It, Cryptography Is Hard, Don't try to invent something yourself, but use a library. There are plenty available.

    There is just absolutely no valid reason to invent it yourself...

    Check This Answer for a breakdown of the different libraries available (that are currently recommended).