I have a system running on PHP version 5.2.10 Unfortunately the original programmer misunderstood how crypt() was implemented.
$crypt = crypt(trim($cuPassword), CRYPT_BLOWFISH);
// The programmer thought this is how you configure a blowfish cipher
nb CRYPT_BLOWFISH has a value of zero on this machine.
This works in as much as it produces a random looking password hash eg 0$oZ534I2VvSw
Today, I migrated the software to PHP 5.4.9 and discovered that $crypt becomes *0 , ie an error due to the invalid salt.
My problem is that I have a table of login passwords that I can no longer validate. My question: Is there going to be a way I can recreate the original cipher that ran under version 5.2? What hash was implemented when you passed "0" as a salt?
Your description doesn't really add up. In PHP 5.4.9, I tested this:
var_dump(crypt('hello', 0));
Output:
0$ny0efnQXFkE
Now in PHP 5.5, you'll get *0 when calling crypt('hello', 0). But that's okay! Because this is still true in PHP 5.5: this crypt('hello', '0$ny0efnQXFkE') == '0$ny0efnQXFkE'.
All you need to do is change how you generate your hash for new passwords. Validating existing passwords will continue to work.
For good measure, after people successfully log in, check if their hash begins with 0$. If it does, rehash the password (since they entered it, you know what it is) with the updated, proper crypt call.
I tried all valid two digit combinations (CRYPT_STD_DES) and I found that "0q" is equivalent (nearly).
PHP 5.2.10
crypt(trim($cuPassword), CRYPT_BLOWFISH);
Result = 0$txv6CWBxJ9Y
PHP 5.4.9
crypt(trim($cuPassword), '0q');
Result = 0qtxv6CWBxJ9Y
All I need to do is adjust the second character and I can match passwords again.
No, there's no way you can recreate the original cipher. Otherwise even a boy scout would be able to break blowfish.
Your best chance is to generate a random password for your users and hash it once again, then force them to change the password as soon as they login.
"$" is not a valid salt value according to crypt(3) so you need to find a crypt implementation that's equally broken as the one PHP/libc used to have :)
If verifying old passwords is enough, use Matthews answer, else try e.g. openssl which currently still seems to accept "0$" as salt:
$ echo -n "secret" | openssl passwd -crypt -salt '0$' -stdin
0$z.PXBBy6uY.
Related
I've spent better half of the day trying to figure out the problem I have, and I'm at a dead end it seems.
I have a ASP application(no access to actual code, just database), in which the user passwords are stored in aspnet_membership > Password column, it also has a salt.
I've also got a copy of the machine key file, which from what I understand contains the keys neede to decryot the password?
<machineKey validationKey="**validation key**" decryptionKey="**decryption key**" validation="SHA1" decryption="AES"/>
i've tried a bunch of different ways of doing this, with open ssl, with different libraries, etc. However I seem to lack knowledge when it comes to this. I'm currently trying to use https://github.com/phpseclib/phpseclib library to decrypt the password:
$cipher = new AES(); // could use AES::MODE_CBC
// keys are null-padded to the closest valid size
// longer than the longest key and it's truncated
//$cipher->setKeyLength(128);
$cipher->setKey(**decrypt key**);
// the IV defaults to all-NULLs if not explicitly defined
$cipher->setIV($salt);
echo $cipher->decrypt($password);
However any way i'm trying todo this, I get either random return or false. I've got a very limited amount of info about the version of AES running on the ASP application or any other encryption info. Any help would be appreciated!
Hi This MachineKey has nothing to do with Salt, the salt is generating by the code at run-time using the Password provided.
.NET framework using Rfc2898DeriveBytes for encryption
Something like this
using (Rfc2898DeriveBytes rfc2898DeriveByte = new Rfc2898DeriveBytes(password, 16, 1000))
{
salt = rfc2898DeriveByte.Salt;
bytes = rfc2898DeriveByte.GetBytes(32);
}
Using the password "testtest" and the hash "KFtIFW1vulG5nUH3a0Mv" with the following code results in different hashes depending on the PHP version (as shown in the image below):
$salt = "KFtIFW1vulG5nUH3a0Mv";
$password = "testtest";
$key = '$2a$07$';
$key = $key.$salt."$";
echo crypt($password, $key);
Output 1 (PHP v.5.3.0 - 5.4.41, 5.5.21 - 5.5.25, 5.6.5 - 5.6.9): $2a$07$KFtIFW1vulG5nUH3a0Mv$.0imhrNa/laTsN0Ioj5m357/a8AxxF2q
Output 2 (PHP v.5.5.0 - 5.5.20, 5.6.0 - 5.6.4): $2a$07$KFtIFW1vulG5nUH3a0Mv$e0imhrNa/laTsN0Ioj5m357/a8AxxF2q
Here is an example of the problem:
http://3v4l.org/dikci
This is a massive issue if crypt is being used to hash passwords for a login, as depending on PHP version the hash will be different. Anybody understand what this issue is from and how to deal with it?
This is not so much an issue as you may think.
First you should note, that your code is not absolutely correct, BCrypt requires a 22 character salt, but you provided a 20 character salt. This means that the terminating '$' (which is not necessary btw) will be seen as part of the salt, as well as the first letter of your password. The $ is not a valid character for a BCrypt salt though.
Another thing to consider is that not all bits of character 22 are used, this is due to the encoding, ircmaxell gave a good explanation about this. So different salts can result in the same hash, you can see this well in this answer. How different implementations handle this last bits of the character 22 could theoretically change. As long as the crypt function can verify the password with both hashes there is no problem.
The generation of the salt with its pitfalls is one of the reasons, why the functions password_hash() and password_verify() where written, they make the password handling much easier.
When I use the following code in a validation form the password matches do not match the two passwords even when the two passwords are same.
$this->form_validation->set_rules('password','Password','required|md5|trim|xss_clean|matches[rpassword]');
$this->form_validation->set_rules('rpassword','Repeat Password','required|md5|trim|xss_clean');
But when I remove the md5 function then the password matches is working properly.
Can someone understand why this happens?
When you do matches[rpassword], it's looking at the current value of password after the md5 but rpassword before the md5.
Switch it to this so that it does the match validation BEFORE converting to md5:
$this->form_validation->set_rules('password','Password','required|matches[rpassword]|md5|trim|xss_clean');
$this->form_validation->set_rules('rpassword','Repeat Password','required|md5|trim|xss_clean');
Also, if this is an application where security truly matters - please know that md5 is very easy to crack and that if someone is able to ever get into your database that they will be able to hack all of these passwords. So basically using md5 is almost the equivalent to not encrypting in the first place.
For password storage, use CRYPT_BLOWFISH or PHP 5.5's password_hash() function. For PHP < 5.5 use the password_hash() compatibility pack.
This is happening because the operation do comparation of "matches[rpassword]" it is before of variable "rpassword" be found, see below:
Your code:
$this->form_validation->set_rules('password','Password','required|md5|trim|xss_clean|matches[rpassword]');
$this->form_validation->set_rules('rpassword','Repeat Password','required|md5|trim|xss_clean');
Updated and working:
$this->form_validation->set_rules('password','Password','required|md5|trim|xss_clean');
$this->form_validation->set_rules('rpassword','Repeat Password','required|md5|trim|xss_clean|matches[password]');
I must migrate my backend from php to node. We used php crypt (with default random salt) to hash the passwords.
For instance, for the password 'd1692fab28b8a56527ae329b3d121c52', I have the following crypted pw in my base (depending if I used either md5 or sha512, as the $i$ specify) :
$1$7JxJYjJK$oFtCGyVvflspPtxB7YrWP.
$6$CVx6KL5l$wzk3YXlqUaz42Kb9r2lmEJhx/FBUXPRoLWN.20/XMBbgQrhp3vSHkEDF3bJEtpM3M96VZ.AMKatLGSKYZZKNH/
And in php I can verify them with crypt :
echo crypt('d1692fab28b8a56527ae329b3d121c52', '$1$7JxJYjJK$oFtCGyVvflspPtxB7YrWP.');
echo "\n";
echo crypt('d1692fab28b8a56527ae329b3d121c52', '$6$CVx6KL5l$wzk3YXlqUaz42Kb9r2lmEJhx/FBUXPRoLWN.20/XMBbgQrhp3vSHkEDF3bJEtpM3M96VZ.AMKatLGSKYZZKNH/');
echo "\n";
Which returns the correct crypted pw.
I did not manage to obtain such results with any node function. I tried stuff like :
require("crypto").createHmac("md5", "7JxJYjJK").update("d1692fab28b8a56527ae329b3d121c52").digest("base64");
And many others, but without any success.
Can someone please help me to do this ? I abolutely need the MD5 version ($1$) ; the sha512 would be somewhat nice (I know it's horrifying, but it's the md5 version that was used on the prod server, and the sha512 that was used on the test server...).
I just converted the original crypt_md5() (as used in PHP) to JavaScript for one of my projects. You can find it here:
https://github.com/BlaM/cryptMD5-for-javascript
(Supports only $1$, but that's at least a part of what you are looking for.)
I have the following snippet of code:
// bcrypt hash of 'password'
$hash = '$2y$10$4u0cQ.WEnwHDo.C5Nl1vm.shKA0beQ32wqzphSfzklAq9OcDM2nLu';
if(password_verify('password', $hash)) {
print_r('woohoo!');
}
else {
print_r('fubar');
}
On one server it's working fine (woohoo!), on another it doesn't work. I've just put it up on codepad.org and it fails there too.
The problem is (as can be see on that codepad page) that the hash computed by crypt is of length 13 instead of the required 60.
I'm using ircmaxel's password_compat library on github to implement the PHP 5.5 only password_verify function.
It seems that you are running the script on a PHP version smaller than 5.3.7, and therefore the algorithm '2y' is not yet known.
If possible, i would consider to do a PHP upgrade on this server, the '2y' parameter solves a problem with unicode input strings.
Should this not be an option, then you can replace the algorithm in the compatibility pack. Somewhere about line 49 you will find...
$hash_format = sprintf("$2y$%02d$", $cost);
...change it to the former BCrypt constant '2a'...
$hash_format = sprintf("$2a$%02d$", $cost);
...this is of course not optimal, but it is the best you can do on earlier versions.
A new generated password hash will now start with '$2a$10$...' and the verification with this hash-value should work on every system.