Salted Password Validation in PHP

2019-07-11 02:28发布

问题:

On crackstation.net it is stated:

To Validate a Password

  • Retrieve the user's salt and hash from the database.
  • Prepend the salt to the given password and hash it using the same hash function.
  • Compare the hash of the given password with the hash from the database. If they match, the password is correct. Otherwise, the
    password is incorrect.

However in the source code listed at the bottom of the page, I can't figure out how the validate_password function takes into account the salt. I mean where is the salt prepended to the given password?

Here is the function in question:

function validate_password($password, $correct_hash)
{
    $params = explode(":", $correct_hash);
    if(count($params) < HASH_SECTIONS)
       return false;
    $pbkdf2 = base64_decode($params[HASH_PBKDF2_INDEX]);
    return slow_equals(
        $pbkdf2,
        pbkdf2(
            $params[HASH_ALGORITHM_INDEX],
            $password,
            $params[HASH_SALT_INDEX],
            (int)$params[HASH_ITERATION_INDEX],
            strlen($pbkdf2),
            true
        )
    );
}

回答1:

Looks like the Salt, Hash and interation number are stored in the same string and are separated into three strings (in an array) at the beginning of the function:

$params = explode(":", $correct_hash);

The order of the values depends on how the constants HASH_ALGORITHM_INDEX, HASH_SALT_INDEX and HASH_ITERATION_INDEX are defined.



回答2:

PHP 5.5 has its own function password_hash() ready to generate secure BCrypt hashes. As in the example above, the resulting string will contain the hash, the salt and the cost parameter and is always 60 characters in length. Those parameters are extracted by the function password_verify() later, to verify the password.

For earlier PHP versions there exists a compatibility pack‌​.



回答3:

I'm not entirely sure what's going on above, so let me explain hashes a bit, this has been done before, and there are sources, but I'll have a go anyway! It was new to us all once!

If we have a function, call it f and let it be our hash function, it takes every possible input to just a finite number of outputs, this is why it can't be reversed, this is called a surjective function http://en.wikipedia.org/wiki/Surjection see there for more. Like f(x)=x^2 if f(x)=4 we don't actually know what x is (we happen to know it can be either 2 or -2 though)

The hash is of a different order though because an infinite number of (theoretical, computers can't handle the longest of the set of all strings, it is too large!) inputs to a finite number of outputs.

If I know it's a pin-number suppose, suppose your pin number is P I know f(P)=h, and P is 1 of 10,000 values, 0000 to 9999 inclusive. So I can just do 10k hashes (worst case) and get your pin.

The salt is some random crap that's deterministically applied to the input, so suppose s(x) salts x, it could be by putting the letter "k" between each letter of the input (this would be a crap salt though) but that way the output of s only depends on x, given the same input we get the same output.

Then you do f(s(x)) to get your hash, this way if I know the hash, but don't know the salt, I'd have to work out how you salted it first, if I did know the salt I'd have to know how you applied it, and so forth.

So suppose you did the simple salt above to passwords, so the common "password" becomes "pkakskskwkokrkd", and I have my "rainbow table" (a list of inputs and their hash values), I probably wont have (the salted form) in that table, but suppose I know the hash, and that you put ks between letters, I'd have to re-create my rainbow table using words but with ks in them and that'd take a lot of memory, a lot of time..... but then I can break all your passwords (because the salt was the same for all)

This is what your system means by "users salt", that way if I break the system with ks only one guy's stuff is cracked, I'd have to do it for everyone.

Now for some PHP code!

I recommend you use a sha512 salt, this gives a fixed 128 character-long (it's PHP, it'll end up in a database) output for any input.

$hash = hash("sha512",$data);

To salt it you can hash it several times, you can put a k between it, as long as it is 'deterministic' the same for a given input.

Lastly how it works:

If the bank has a salted hash of my pin number, they don't "break" it, to get my pin number to compare the number I entered with them, they salt and hash the pin number I just put in, and compare the value with the result in the database, if the hashes are the same, the pins entered are the same.

Hashes can collide but this is VERY rare, the "Avalanche Effect" was studied and made sure to be present (terminology?) in hashing algorithms, a similar input gives a wildly different output.

Hashes are only good for equality tests.