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
Related
Specifically, when using the PASSWORD_DEFAULT algorithm, as per the php page
"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)."
and
"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 7.5.5, it would not be eligible for default until 7.7 (since 7.6 would be the first full release). But if a different algorithm was added in 7.6.0, it would also be eligible for default at 7.7.0.
The default should only change in a full release (7.3.0, 8.0.0, etc) and not in a revision release. The only exception to this is in an emergency when a critical security flaw is found in the current default."
So, when upgrading PHP to a version that uses a different algorithm for PASSWORD_DEFAULT, does that prevent users from logging in if their passwords were hashed with the old algorithm?
The short answer is yes, it is backwards compatible, providing your storage allows for longer password hash lengths.
When you generate a password you will get it in a specific format.
I've generated two passwords using bcrypt and the newer argon2i hash. (Coming in PHP 7.2 with the introduction of libsodium)
FYI: My code here is based upon the libsodium extension since I haven't got php 7.2 downloaded, and wasn't going to install sodium_compat just for the aliases. The php 7.2 syntax will not contain any namespaces.
$bcrypt = password_hash('insecurepassword', PASSWORD_BCRYPT);
$argon2i = \Sodium\crypto_pwhash_str('insecurepassword', \Sodium\CRYPTO_PWHASH_OPSLIMIT_INTERACTIVE, \Sodium\CRYPTO_PWHASH_MEMLIMIT_INTERACTIVE);
var_dump($bcrypt, $argon2i);
The output I got when I ran that was this
string(60) "$2y$10$jiT0NF3u426kguHes8ZBputRE/n9OSdPi5HhHvEWW4mX1XDwKwy1e"
string(96) "$argon2i$v=19$m=32768,t=4,p=1$Ho4Vzgp5nzQkLlp99P+ViA$bDqX8UUlSnfLRCfFBzBnFhWr/hzHzuuUCfZ0LSIns64"
Both passwords are in the same format more or less. If you explode the $ you end up with each piece of the puzzle required to verify the password.
The first section contains the algorithm. 2y for bcrypt, argon2i for... argon2i. (surprise!)
The next bits contain the configuration options, so in bcrypt's case the cost. In argon2i's case, there's some extra configuration.
The final section contains the actual password and salt for the algorithm to check against.
For a bit more of a visual breakdown checkout the php docs.
Even though I've used some different functions to demo some output, there is an rfc that was accepted that introduced support for the argon2i hash to the password_hash functions in 7.2.
So when that time comes, password_verify will "just work" regardless of if you give it a bcrypt or argon2i hash to verify a password against.
Is a one-way hashing algorithm, you can't decrypt hashes.
Use password_verify to check, if the password matches the stored hash:
<?php
$hash = 'your-hash';
if (password_verify('pass', $hash)) {
echo 'Password is valid';
} else {
echo 'Password is not valid!';
}
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.
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.
From password_hash() function:
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).
Does this means whenever PASSWORD_DEFAULT changes i wont be able to use the new php versions because otherwise password_verify() won't be able to correctly check the old user password?
No, password_verify() will recognise the algorithm used, because it's embedded in the hash that you're verifying against: that's what the first part of the hash (e.g. $2y$) indicates
The question is the same title, Where $2x$ is used in BCrypt?
The following scenario is right?
We have a set of passwords that hashed with $2a$ prefix already, when the Server PHP version was earlier 5.3.7. Now we upgraded the PHP to 5.3.7+, now we must firstly verify previous passwords with $2x$ algorithm then rehash the password with $2y$ prefix. That's right?
BCrypt variants
$2$
BCrypt was designed by the OpenBSD people. It was designed to hash passwords for storage in the OpenBSD password file. Hashed passwords are stored with a prefix to identify the algorithm used. BCrypt got the prefix $2$.
This is in contrast to the other algorithm prefixes:
$1$: MD5
$3$: NTHASH
$5$: SHA-256
$6$: SHA-512
$7$: scrypt
$2a$
The original BCrypt specification did not define how to handle non-ASCII characters, or how to handle a null terminator. The specification was revised to specify that when hashing strings:
the string must be UTF-8 encoded
the null terminator must be included
$2x$, $2y$ (June 2011)
A bug was discovered in crypt_blowfish🕗, a PHP implementation of BCrypt. It was mis-handling characters with the 8th bit set.
They suggested that system administrators update their existing password database, replacing $2a$ with $2x$, to indicate that those hashes are bad (and need to use the old broken algorithm). They also suggested the idea of having crypt_blowfish emit $2y$ for hashes generated by the fixed algorithm. Nobody else, including canonical OpenBSD, adopted the idea of 2x/2y. This version marker was was limited to crypt_blowfish🕗.
The versions $2x$ and $2y$ are not "better" or "stronger" than $2a$. They are remnants of one particular buggy implementation of BCrypt.
$2b$ (February 2014)
A bug was discovered in the OpenBSD implementation of BCrypt. They were storing the length of their strings in an unsigned char. If a password was longer than 255 characters, it would overflow and wrap at 255.
BCrypt was created for OpenBSD. When they have a bug in their library, they decided its ok to bump the version. This means that everyone else needs to follow suit if you want to remain current to "their" specification.
http://undeadly.org/cgi?action=article&sid=20140224132743 🕗
http://marc.info/?l=openbsd-misc&m=139320023202696 🕗
The version $2b$ is not "better" or "stronger" than $2a$. It is a remnant of one particular buggy implementation of BCrypt. But since BCrypt canonically belongs to OpenBSD, they get to change the version marker to whatever they want.
There is no difference between 2a, 2x, 2y, and 2b. If you wrote your implementation correctly, they all output the same result.
And if you were doing the right thing from the beginning (storing strings in utf8 and also hashing the null terminator) then: there is no difference between 2, 2a, 2x, 2y, and 2b. If you wrote your implementation correctly, they all output the same result.
The only people who need to care about 2x and 2y are those you may have been using crypt_blowfish. And the only people who need to care about 2b are those who may have been running OpenBSD.
All other correct implementations are identical and correct.