crypt() outcome different between PHP versions [duplicate] - php

I used crypt() to hash password, with a blowfish salt like this:
$2a$, 2 digits, $, 21 chars in [a-zA-Z0-9]
Here I made a mistake that chars length after third $ is 21 not 22. But it worked fine so I didn't find the error.
It works on my desktop which running windows and php 5.4.4 and on AWS ec2 which running Amazon linux with php 5.3.x, with that too short salt.
One day I updated AWS php to 5.5.14. then the problem occurred. crypt() return *0 all the time.
After some try, I added a $ at end of the salt so , it become 22 chars. And it works again and return the same hash string as before. Although it doesn't obey the blowfish rule, chars should be [./a-zA-Z0-9]
But now I duplicate this site to another machine which running openSuSE 13.1 with php 5.5.14, This salt failed again.
I downgrade php to 5.4.20 but not help.
The new site still need old database so I have to make that password hash works.
What is the library or module that effect this blowfish salt length error compatibility issue? Tt seems not PHP's version. AWS 5.5.14
Or is there another magic char can save me again? I tried replace th tail $ to each one in [./a-zA-Z0-9] but no lucky, the hash string is different ....

First i would strongly recommend to use the new functions password_hash() and password_verify() to generate and verify new hashes. Of course this doesn't solve your actual problem with the old hashes, but it may be a good idea to mark them as old, so they can be updated the next time the user logs in.
For this old hashes i would try to verify them, generating a salt with a valid last character 22. The crypt function does actually use only part of the bits of character 22 (126 bits of the salt instead of 128). So groups of the last character 22 will end up in the same hash-value.
See the answer to this question Why does crypt/blowfish generate the same hash...
If you try out all relevant characters [.Oeu] as the character 22, the chance is good that one combination will generate the same result as your invalid salt.
EDIT:
Since the used salt becomes part of the password-hash, you should be able to see what was used as character 22 (the 22th character after the third $).

Using (again) '$' as the last character should make your passwords work if you downgrade to PHP 5.4.
This is however, not a long-term solution. Using '$' as the last character has made all your passwords forward-incompatible, because that's not a valid Base64 character (regardless of whether that's regular or bcrypt-compatible Base64).
For as long as you can use PHP 5.4, and that means as long as PHP 5.4 is officially supported, you should re-hash all old passwords whenever they are used.
After PHP 5.4 support is dropped, you'll have no other choice but to just generate new random passwords for your users who've remained with the old hashing scheme and e-mail them.
I must also suggest that you use the password-compat package for your updated passwords. It will give you the password_*() functions that are otherwise available only on PHP 5.5+. The package's author is the same person who implemented the functions in PHP itself, so you can be sure that it is both safe and 100% compatible, providing you with forward-compatibility when you upgrade to 5.5+.

Related

Is PHP's password_hash() Backwards Compatible?

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!';
}

php crypt() blowfish salt length backward compatible

I used crypt() to hash password, with a blowfish salt like this:
$2a$, 2 digits, $, 21 chars in [a-zA-Z0-9]
Here I made a mistake that chars length after third $ is 21 not 22. But it worked fine so I didn't find the error.
It works on my desktop which running windows and php 5.4.4 and on AWS ec2 which running Amazon linux with php 5.3.x, with that too short salt.
One day I updated AWS php to 5.5.14. then the problem occurred. crypt() return *0 all the time.
After some try, I added a $ at end of the salt so , it become 22 chars. And it works again and return the same hash string as before. Although it doesn't obey the blowfish rule, chars should be [./a-zA-Z0-9]
But now I duplicate this site to another machine which running openSuSE 13.1 with php 5.5.14, This salt failed again.
I downgrade php to 5.4.20 but not help.
The new site still need old database so I have to make that password hash works.
What is the library or module that effect this blowfish salt length error compatibility issue? Tt seems not PHP's version. AWS 5.5.14
Or is there another magic char can save me again? I tried replace th tail $ to each one in [./a-zA-Z0-9] but no lucky, the hash string is different ....
First i would strongly recommend to use the new functions password_hash() and password_verify() to generate and verify new hashes. Of course this doesn't solve your actual problem with the old hashes, but it may be a good idea to mark them as old, so they can be updated the next time the user logs in.
For this old hashes i would try to verify them, generating a salt with a valid last character 22. The crypt function does actually use only part of the bits of character 22 (126 bits of the salt instead of 128). So groups of the last character 22 will end up in the same hash-value.
See the answer to this question Why does crypt/blowfish generate the same hash...
If you try out all relevant characters [.Oeu] as the character 22, the chance is good that one combination will generate the same result as your invalid salt.
EDIT:
Since the used salt becomes part of the password-hash, you should be able to see what was used as character 22 (the 22th character after the third $).
Using (again) '$' as the last character should make your passwords work if you downgrade to PHP 5.4.
This is however, not a long-term solution. Using '$' as the last character has made all your passwords forward-incompatible, because that's not a valid Base64 character (regardless of whether that's regular or bcrypt-compatible Base64).
For as long as you can use PHP 5.4, and that means as long as PHP 5.4 is officially supported, you should re-hash all old passwords whenever they are used.
After PHP 5.4 support is dropped, you'll have no other choice but to just generate new random passwords for your users who've remained with the old hashing scheme and e-mail them.
I must also suggest that you use the password-compat package for your updated passwords. It will give you the password_*() functions that are otherwise available only on PHP 5.5+. The package's author is the same person who implemented the functions in PHP itself, so you can be sure that it is both safe and 100% compatible, providing you with forward-compatibility when you upgrade to 5.5+.

Where 2x prefix are used in BCrypt?

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.

PHP - Replacing mcrypt_create_iv() with openssl_random_pseudo_bytes()

I need to up the security of our website, and is currently using the guide here: http://crackstation.net/hashing-security.htm, and also the generation of random passwords here: https://defuse.ca/generating-random-passwords.htm. I gather that both uses the function mcrypt_create_iv() for generating random bytes (or bits?), but for some reason, I encounter errors in installing php-mcrypt under CentOS 6. Fortunately, the first link said that openssl_random_pseudo_bytes() is a CSPRNG (and the PHP documentation and other sources also back that claim), and is available on the current server installation of PHP 5.4, so I have no choice but to use that at the moment. With these in mind, I would like to ask the following:
Does a direct code substitution suffice without affecting security? (That is, just replacing calls to mcrypt_create_iv() to openssl_random_pseudo_bytes() would do?)
About the constants mentioned in the code (http://crackstation.net/hashing-security.htm#properhashing), the guide says that "[m]ake sure your salt is at least as long as the hash function's output." Am I right in assuming that PBKDF2_SALT_BYTES and PBKDF2_HASH_BYTES are both set to 24 bytes since the output of the pbkdf2() function would be just 24 bytes, not 32 (for 256 bits) since the underlying algorithm used is sha256? (Yes, I am using key stretching too.) In a related note, is 24 bytes fine, or should be increased/decreased, and what effect would that have?
Advanced thanks for those who will answer.
I think the security will not be affected because both functions are just cryptographically secure pseudorandom number generators (NB: openssl_random_pseudo_bytes($len, true) and mcrypt_create_iv($len, MCRYPT_DEV_RANDOM)).
PBKDF2_SALT_BYTES is used only in the test function create_hash() and not in pbkdf2() itself. So you just need to implement your own salt generation function using those CSPRNGs.

Do you recognize this password hashing format?

I'm trying to reverse-engineer a password scheme on a legacy PHP application, so I can port the passwords to a new system which will be replacing it. The application has passwords stored in two formats, a newer and an older one. The newer one simply uses crypt() with salt. The older one doesn't seem to have any supporting code any more (at least not in version control), and no hint of what may have been used. I have one account in the old style for which I may know the password, but I don't know how to check it.
The password is stored in the following format:
$1$f1KtBi.v$nWwBN8CP3igfC3Emo0OB8/
It appears to be three fields, delimited by $: 1, f1KtBi.v, and nWwBN8CP3igfC3Emo0OB8/. The first field is always 1. The second and third fields always match the regular expression [a-zA-Z0-9/.]+. The second field always has 8 characters, the third field always has 22.
Have you seen this password storage scheme before? Any idea what hashing mechanism might have been used?
This is the output of the crypt()[docs] function.
The 1 means that it used the MD5 algo internally.
That's most likely produced with crypt(), especially with MD5:
CRYPT_MD5 - MD5 hashing with a twelve character salt starting with $1$
Good luck finding the salt.

Categories