0

I use PHP's PASSWORD_DEFAULT as the hashing Algorithm; now, I noticed that, if I use it with a salt, only the first 8 chars are verified.

Here is a bit of code I wrote to test that:

<?php 
    $test_pw = "%ImAVery1234Secure!Password$";
    $test_pw_to_be_hashed = "%ImAVery";

    //

    $salt = bin2hex(openssl_random_pseudo_bytes(32));
    $password = $salt.$test_pw;
    $password_hashed = password_hash($salt.$test_pw_to_be_hashed, PASSWORD_DEFAULT);
    echo password_verify($password, $password_hashed);
?>

This returns 1 for me. If I remove one more chars from the test_pw_to_be_hashed value, it returns 0.

Is there any way to hash the whole password? Do I have to use another hashing algorithm?

If I have to use another hashing algorithm, is there any way to check it with PHP's password_verify method, or do I have to "re-hash" it and then just check both values like below?

if(password_hash($salt.$test_pw_to_be_hashed, OTHER_ALGO) == $db_password)

If I have to change the hashing algorithm, is there any way to re-hash the passwords used currently, or do I have to hash them again when I have the plain text password (when a user logs in again)?

Adrian Mole
  • 49,934
  • 160
  • 51
  • 83
  • 2
    Why are you trying to add your own salt at all? `password_hash()` does this automatically – GrumpyCrouton Dec 30 '22 at 13:59
  • 1
    Your salt is already adding 64 chars, so how many do you need? (especially as pointed out, you don't need it). – Nigel Ren Dec 30 '22 at 14:00
  • 4
    Your random salt is changing the password. So password_verify() will always fail. – Jason K Dec 30 '22 at 14:11
  • 1
    There is [maximum length](https://stackoverflow.com/questions/21479655/maximum-length-of-generated-hash-when-using-password-hash) for the password and your salt is over limit then the rest of raw password will be truncate that's why. – vee Dec 30 '22 at 14:15
  • @GrumpyCrouton thank you, i didn't know, that it already adds a salt. But then it solves that problem for me, thank you! – CuzImBisonratte Dec 30 '22 at 14:18
  • @JasonK Because i didn't know that password_hash() is adding a salt automatically i always had stored it in a database. – CuzImBisonratte Dec 30 '22 at 14:19
  • @vee Is there a way to get more characters? Do i just have to use another hashing algorithm than the standart or is it a limit for all hashing methods in php? – CuzImBisonratte Dec 30 '22 at 14:20
  • @CuzImBisonratte It is PHP limited, you can't do anything. In fact, 60 characters is much enough and too much for human to enter their password. Check characters length before hash and save password in DB. – vee Dec 30 '22 at 14:24

1 Answers1

3

The built-in function password_hash already generates a random hash for you. It returns a string containing the algorithm identifier, the salt and the cryptographic hash. This complete string is confusingly called "hash" itself in the PHP docs. In fact it is a composition containing the hash.

The function password_verify can identify the hash algorithm, the salt and the hash from the string generated by password_hash.

$hash_from_db = '$2y$10$1Ow3T9597X1e9W8dtVbKK.VAAo6Op6xIbglp.3amRCSVgLlTevhjS';
$test_pw      = '%ImAVery1234Secure!Password$';

// this gives another hash each time due to a random salt
echo password_hash($test_pw, PASSWORD_DEFAULT), PHP_EOL;

// this verifies against a strored hash
echo password_verify($test_pw, $hash_from_db) ? 'correct' : 'incorrect';

A cryptographic oneway hash function is meant to be irreversible by design. Thus there is no way to rehash older hashes directly. You have to wait until the next login. Then you can check whether the hash in the database is compliant to the current security standard. Older algorithms will still work. First check as usual, whether the provided password is correct. Then recreate a new hash from the given plain password.

If you for some reason do want an additional own long salt string, you have to store that along with the hash as well. When verifying, you need to use that same salt with the user provided password in the same way as you have built the hash input before and pass the combined string to the password argument of the password_verify function.

Since some crypto algorithm might limit the length of the password input, it is a good idea to append further salt strings to the end of the password rather than prepending. Otherwise in the worst case the verification would always be true when the input is truncated to a shorter length than the length of a prepended salt.

As stated in the password_hash PHP docs

Caution Using the PASSWORD_BCRYPT as the algorithm, will result in the password parameter being truncated to a maximum length of 72 bytes.

bcrypt is the current default algorithm used by password_hash. Thus prepending instead of appending a longer salt would counteract the security.

Though building a longer hash from the generated hash by an own implementation would be possible, cryptography is a complex sciency and custom implementations will most likely introduce more security holes (e.g. timing attacks) rather than increasing security. Use the options provided by PHP's implementations instead, e.g. adjust the cost of the calculation as documented in Predefined Constants. A calculation time of about 3-6 seconds is a fair compromise. This is usually done only once per session and the session is secured by a less secure session id. Consider to reask the password when accessing sensitive data like password change, critical personal information aso.

Keep in mind that even a strong password hash algorithm is considered as not secure enough. Consider to implement multi factor authentication, e.g. sending a TAN to a mail address or mobile phone or even better supporting a cryptographic hardware dongle. (This does not replace but extend password security!)

Pinke Helga
  • 6,378
  • 2
  • 22
  • 42
  • Where does PHP's `password_hash()` store the salt? Because what i am currently thinking of, would be storing it somewhere in the PHP files itself, but afaik there is no problem moving between hosts or PHP executables. So that is something it cant store its salts at, right? – CuzImBisonratte Dec 30 '22 at 16:23
  • 1
    The salt is encoded into the "hash" string (better said the composition) itself. `$2y$10$` identifies the bcrypt algorithm with 10 rounds. Then the salt follows. The PHP functions know the lenghts, there is no further `$` delimiter anymore. Finally the hash follows. Migration should not be any problem, except a new PHP version breaks the backward compatibility. – Pinke Helga Dec 30 '22 at 16:32
  • @CuzImBisonratte see also my last paragraph just added when you are going to implement strong authentication methods. – Pinke Helga Dec 30 '22 at 16:53