How can PHP Hash method change? [duplicate] - php

This question already has an answer here:
password_hash() PASSWORD_DEFAULT PHP 5.5
(1 answer)
Closed 8 years ago.
On PHP's site here: http://php.net/manual/en/password.constants.php, this following is stated:
PASSWORD_DEFAULT (integer)
The default algorithm to use for hashing if no algorithm is provided. This may change in newer PHP releases when newer, stronger hashing algorithms are supported.
It is worth noting that over time this constant can (and likely will) change. Therefore you should be aware that the length of the resulting hash can change. Therefore, if you use PASSWORD_DEFAULT you should store the resulting hash in a way that can store more than 60 characters (255 is the recomended width).
How can this be? If someone sets their password, and the hash is set in the database, and then the method changes, they will not be able to get in, since the method will produce a different hash, will it not?

When you hash a password using the hash_password() function, information about the used algorithm and cost is included in the return string. Therefore, password_verify() can always check whether a provided password is valid given a certain hash.
See the docs for password_hash():
The used algorithm, cost and salt are returned as part of the hash. Therefore, all information that's needed to verify the hash is included in it. This allows the password_verify() function to verify the hash without needing separate storage for the salt or algorithm information.
There's also the function password_needs_rehash() which can be used to check whether a certain hash has been computed with an old algorithm, in which case a new hash has to be computed. Since at the time of a login the password is available as plaintext, you can (and should) at that moment rehash the password if needed.
The warning in the docs about the value changing over time is to make users aware that the length of the computed hash may change. However, the functions are intended to be backwards compatible with older (typically shorter) hashes.

Related

Why is PHP's hashing Algo not working right?

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)?
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!)

Using password_hash PASSWORD_DEFAULT legacy concerns

From the php page: http://php.net/manual/en/function.password-hash.php
PASSWORD_DEFAULT - Use the bcrypt algorithm (default as of PHP 5.5.0). Note that this constant is designed to change over time as new and stronger algorithms are added to PHP. For that reason, the length of the result from using this identifier can change over time. Therefore, it is recommended to store the result in a database column that can expand beyond 60 characters (255 characters would be a good choice).
Would this mean that if I password_verify a password with a later version e.g. PHP8, it might not be able to decipher the stored password correctly? Or is all that information safely embedded already and all I have to do is store the output of password_hash into my DB and just call password_verify for validation without worries?
Would this mean that if I password_verify a password with a later version e.g. PHP8, it might not be able to decipher the stored password correctly?
No. The password hash includes information on which specific algorithm was actually used. If a future version of PHP supports more than one password hashing algorithm, it will be able to read that information from an old hash to figure out how to reproduce it.
(Specifically, the $2y$ prefix currently present on all hashes indicates that they were generated using the PASSWORD_BCRYPT algorithm. Any future algorithm will use a different prefix.)
Yes, all the information is safely embedded, provided that your database doesn't truncate the encrypted password. Which is pretty much a given, I'd think.
What that warning is driving home is that, for your future self's sanity, you store the information in a field whose length is flexible. Historically, like with MD5, one might have chosen CHAR(32). But with bcrypt, you need to choose something more flexible. That might be:
VARCHAR(255)
CHAR(60), with this column being the last one in the table, so that extending its length might not require the RDBMS to reshuffle table.

If the default password_hash hash gets updated in php, does this affect future login attempts by a user?

Let's say you're using the password_hash and password_verify functions that are built into php and using the default hashing algorithm (currently bcrypt). Now, given that this is the default, and if this changes, this would affect the password_verify function in the future, would it not? So, would a programmer need to write code to check for this in the future, ie, if PASSWORD_DEFAULT != bcrypt, use bcrypt to continue the verification process, and then also update the database given the new algorithm?
No, changing the algorithm or the cost will not affect password_verify since algorithm and cost is part of the stored hash. If you see specs for password_hash it state
The used algorithm, cost and salt are returned as part of the hash. Therefore, all information that's needed to verify the hash is included in it. This allows the password_verify() function to verify the hash without needing separate storage for the salt or algorithm information.
You can use password_needs_rehash http://php.net/manual/en/function.password-needs-rehash.php to check if you need to update the stored password. So PHP do already support all of this for you, making hashing the passwords in a secure way easy.

What will happen if they changed PASSWORD_DEFAULT in PHP Password library?

Consider this line of code by using PHP:
$password = password_hash($password, PASSWORD_DEFAULT);
What will happen if they changed the default password hashing algorithm? I mean I will be having hashed password inside the database. Then, from my own understanding, it will be impossible to check the password because the hashing algorithm will be totally changed.
What would happen is that newly-hashed passwords will be using the new algorithm - obviously.
However, you shouldn't be concerned about this, because the whole thing is designed with forward-compatibility in mind - your code won't be broken when the default algorithm changes, as long as you're using the password_*() functions correctly.
By correctly, I mean use password_verify().
password_verify() accepts a plain-text password and a hash, and it can easily determine what the used algorithm is by looking at the hash that you feed it. Therefore, it would also still be able to verify a password that has been hashed using an old algorithm - not just the previous one, but any algorithm that is supported.
In fact, the sole purpose of the PASSWORD_DEFAULT constant is that you can easily migrate older hashes to a new algorithm (once one is added). This happens the following way:
When a user logs in, you verify their password via password_verify() (any hashing algorithm that has a PASSWORD_<name> constant will work).
You call password_needs_rehash(), and if the password you just verified is using an older algorithm (or a lower 'cost' paramater) - it will return boolean TRUE.
If boolean TRUE was indeed returned, you can now replace the old hash with one that uses the new algorithm; you can do that during a login, because the user just gave you the password and you verified that it is correct.
In summary - it's a really, really well-designed API and it solves problems for you that you haven't even thought about. Don't worry about it.
Edit (noted in the comments):
It should be noted, however, that new algorithms will quite probly result in longer hash lengths, so if you're storing the passwords in a database - don't limit the field's length (i.e. use a varchar(255) field).
Just for clarification, I would like to add to the answer that PHP uses the following structure. Thus, the password_needs_rehash() and password_verify() functions will check the algorithm and cost and do their work to keep everything compatible and correct.
Source: http://php.net/manual/en/faq.passwords.php

What is the default algorithm in password_hash

Reading the documentation about a new password_hash function for PHP 5.5, I am wondering, what is the default algorithm:
password_hash("rasmuslerdorf", PASSWORD_DEFAULT);
Documentation about it does not clarify this: http://www.php.net/manual/en/password.constants.php
I have had a look into the PHP source code. It defaults to bcrypt in PHP5.5.
From ext/standard/php_password.h line 31:
#define PHP_PASSWORD_DEFAULT PHP_PASSWORD_BCRYPT
This has been updated in the documentation at password_hash() and will be updating shortly in the constants page (I just committed the documentation change about an hour or so ago).
This will be live today at password.constants
From the updated constants page (which hasn't gone live yet, but will be later today):
Available algorithms:
PASSWORD_BCRYPT (integer)
PASSWORD_BCRYPT is used to create new password hashes using the CRYPT_BLOWFISH algorithm.
This will always result in a hash using the "$2y$" crypt format, which is always 60 characters wide.
Supported Options:
salt - to manually provide a salt to use when hashing the password. Note that this will override and prevent a salt from being automatically generated.
If omitted, a random salt will be generated by password_hash() for each password hashed. This is the intended mode of operation.
cost - which denotes the algorithmic cost that should be used. Examples of these values can be found on the crypt() page.
If ommitted, a default value of 10 will be used. This is a good baseline cost, but you may want to consider increasing it depending on your hardware.
PASSWORD_DEFAULT (integer)
The default algorithm to use for hashing if no algorithm is provided. This may change in newer PHP releases when newer, stronger hashing algorithms are supported.
It is worth noting that over time this constant can (and likely will) change. Therefore you should be aware that the length of the resulting hash can change. Therefore, if you use PASSWORD_DEFAULT you should store the resulting hash in a way that can store more than 60 characters (255 is the recomended width).
Values for this constant:
PHP 5.5.0 - PASSWORD_BCRYPT
As far as when and how PASSWORD_DEFAULT will be updated, that's on the password_hash() documentation page:
Note: Updates to supported algorithms by this function (or changes to the default one) must follow the following rules:
Any new algorithm must be in core for at least 1 full release of PHP prior to becoming default. So if, for example, a new algorithm is added in 5.5.5, it would not be eligible for default until 5.7 (since 5.6 would be the first full release). But if a different algorithm was added in 5.6.0, it would also be eligible for default at 5.7.0.
The default should only change on a full release (5.6.0, 6.0.0, etc) and not on a revision release. The only exception to this is in an emergency when a critical security flaw is found in the current default.
The documentation is actually rather specific, if a bit poorly worded; the hash is the strongest one PHP believes it has available at the time, and is subject to change at any time. The hashes produced by password_hash contain a bit of data at the start that indicates which has was initially used to produce them, allowing such upgrades to occur automatically as new hash algorithms become available, without breaking any hashes you have already stored in a database.
Since bcrypt is the only algorithm currently defined, you can probably assume it's the default, but a quick way to verify would be to make a simple PHP script that hashes the same string twice, once with each option, and with a fixed salt, and prints the resulting hashes; they will probably match.
The original password_hash spec may also be of some help. https://wiki.php.net/rfc/password_hash

Categories