YII2 crypt function that did work has stopped - php

I have a page in my site that allows the user to change there password but as part of the form they have to enter the old password. In the model I have verification to check the old password entered on the form matches the one in the database
public function oldPasswordCheck($attribute, $params)
{
$old_password = $this->attributes['password'];
if (crypt($this->old_password, $old_password) != $old_password)
{
$this->addError('old_password', 'This is not the old password');
}
}
This worked until recently but for some reason now, if you enter the correct old password, it still tells you they didn't match.
Any pointers would be much appreciated.

Some possible answers:
1) Your salt parameter
Php crypt() function uses two parameters: crypt ( string $str [, string $salt ] ). It seems that you are using the password as salt, which is a very bad practice. Is that so or are you just confusing the salt parameter? I would also recommend to always compare passwords with php hash_equals(), because crypt() is vulnerable to timing attacks.
2) changes in your server configuration
What type of hash your php configuration use to crypt/decrypt strings?:
On systems where the crypt() function supports multiple hash types, the following constants are set to 0 or 1 depending on whether the given type is available: CRYPT_STD_DES, CRYPT_EXT_DES, CRYPT_MD5, CRYPT_BLOWFISH, CRYPT_SHA256 and CRYPT_SHA512. If you changed recently your server configuration, some of that could not be available anymore. Check it with phpinfo()
A recommendation
You are working with a framework, and Yii like every framework has its own class to crypt and decrypt strings. Take a look at yii security class. It will make your life easier.

Related

I can't compare password from my database and the one inputted

I am using php crypt function to make a password secure, but when I try and compare a password entered to a one in the database it will not work.
here is my code to create the password in the first place:
$crypt_password = crypt($_POST['confirm-password']);
here is me trying to compare to the password in another function:
$input_crypt_password = crypt($_POST['input-pw']);
if ($input_crypt_password == $dbpassword){
// do change password function
}
This is not working.
when i print both passwords the are different.
why are the passwords different even though I am entering the same password and using crypt function on both?
can anyone point me in the right direction?
From the docs
Example #1 crypt() examples
<?php
$hashed_password = crypt('mypassword'); // let the salt be automatically generated
/* You should pass the entire results of crypt() as the salt for comparing a
password, to avoid problems when different hashing algorithms are used. (As
it says above, standard DES-based password hashing uses a 2-character salt,
but MD5-based hashing uses 12.) */
if (hash_equals($hashed_password, crypt($user_input, $hashed_password))) {
echo "Password verified!";
}
?>
The code in the question will effectively generate a new hash every time it's called - the existing password hash needs to be passed as the salt to get a consistent result.
As also mentioned in the docs:
Use of password_hash() is encouraged.
I'd go further and say you definitely should be using password_hash instead of calling crypt for password usage (assuming php >= 5.5); in any case though for whichever whatever tools/methods you're using - please read the docs to know how to use them.
Don't use crypt directly for passwords.
If you have PHP 5.5+, than use the built in password_hash function, otherwise if you have PHP 5.3.7+ use the polyfill for this function.
Try to something like this.
$crypt_password = crypt($_POST['confirm-password'],salt);
$input_crypt_password = crypt($_POST['input-pw'],salt);
if ($input_crypt_password == $dbpassword){
// do change password function
echo "Password match successfully!";
}
Here salt parameter to base the hashing on. If not provided, the behaviour is defined by the algorithm implementation and can lead to unexpected results.
I don't know what to say that will add more detail than what everyone else has already said...
So, in modern day hash/unhashing algorithms it would be unsafe to store passwords using standard hashing functions (e.g. MD5 / SHA256) as it is quick and easy to unhash this type of entry.
password_hash() as referenced in other answers and comments should be you're #1 way to safely store passwords as it uses a one way hashing algorithm.
You should read this page!
And then in response to your original question, use hash_equals() function to compare passwords.
As many guys here said, you should use password_hash php function.
Here you can see a simple example how to use it:
<?php
$password = '123456';
$userInput = '123456';
$storedHash = password_hash($password, PASSWORD_DEFAULT);
if (password_verify($userInput, $storedHash)) {
echo 'OK';
} else {
echo 'ERROR';
}
Also as mentioned before, if you use older version of PHP, you can install polyfill.
Did you trim the input before saving in db and while making the comparison. Since the input is coming from browser this may be a reason why it is not matching. otherwise this https://stackoverflow.com/a/41141338/1748066 seems appropriate.

Convert PHP's password_verify to Ruby on Rails

We're in the process of converting our site from an old PHP framework to Rails, and would really like for users to continue being able to login with their old password. On the old site, we're using password_hash and password_verify to hash and verify the passwords. However, on Rails I can't seem to get it to verify the old password.
Here is what we have in PHP:
Hash:
password_hash($user['salt'] . $password . $user['salt'], PASSWORD_DEFAULT);
Verify:
password_verify($user['salt'] . $password . $user['salt'], $user['password'])
On the new Rails framework we're using Devise and have built a custom migration script to move everything over and identify the correct password hashing method based on a password_version stored in the db, and this is what I'm using inside my User model:
def valid_password?(password)
if password_version == 'legacy'
hash = BCrypt::Password.new(encrypted_password)
hash_str = password_salt+password+password_salt
return hash.is_password? hash_str
end
super(password)
end
Any ideas would be greatly appreciated
The format of a PHP password_hash password looks roughly like this:
$2y$10$.vGA1O9wmRjrwAVXD98HNOgsNpDczlqm3Jq7KnEd1rVAGv3Fykk1a
The default Ruby Bcrypt method produces passwords of the form:
$2a$10$GtKs1Kbsig8ULHZzO1h2TetZfhO4Fmlxphp8bVKnUlZCBYYClPohG
For a clean solution here you can always differentiate between the two by the $2y or $2a prefix. There's no need for a format column when it's already baked into the format.
For example:
case (encrypted_password[0,3])
when '$2y'
# Legacy PHP password
BCrypt::Password.new(encrypted_password.sub(/\A\$2y/, '$2a')).is_password?(salt + password + salt)
when '$2a'
# Ruby BCrypt password
BCrypt::Password.new(encrypted_password).is_password?(password)
else
# Unexpected type?
end
What you'll want to do on a successful verification of password is re-write the password to the database using Ruby's method to gradually replace all the old PHP formatted ones.

md5 Decryption in PHP

I am developing a system for Online Hotel booking system which I did not start from scratch. The information of customers(bookers) in the system are encrypted using MD5 but unlike normal md5() php function the system is quite complicated as you can see here :
$psw = md5("vhdsxnjuobef");
$t_cred_num = md5_encrypt($t_cred_num, $psw, 16);
and for Decryption it goes like :
$psw = md5("vhdsxnjuobef");
$t_credit_num = md5_decrypt($t_cred_num, $psw, 16);
this code is not Working though on my Server and there is alot of Customer's information Encrypted.
Example of hash of t_cred_num variable =>
fdRucZHctr7vIX+U400xGHq53Qemze0YQH1sAUjvmaC1P+XaRadI9CaX0wrkDXu6
Any Ideas on how to Decrypt these hashes ? When I use md5_decrypt with the hashes nothing happens.
I think the md5_crypt and md5_encrypt are functions hand crafted by the previous developer. md5 isn't supposed to be decryptable. Hash's are supposed to be one way functions: http://en.wikipedia.org/wiki/Hash_function
So, you'll need to find the definition of those functions. A search for "function md5_" in the code files should find the place in the code where they are defined.
there's no way to decrypt an md5. there are two things you can do:
if you have other account or know the password of another account then copy the md5 characters of it and place it on yours.
search for an md5 of let's say "admin". Paste the md5 equivalent of the word "admin" on your account. You can also use other words.
After that, login to your account then change back your password

How best to upgrade to password_* functions from hash('sha512','salt')

I am keen to migrate my code to the new password_* functions provided natively by PHP.
The existing hashes in the database have been generated as follows:
hash ('sha512', '<A constant defined earlier>' . $email . $password);
I'd like to move these to be hashes created by the now-recommended:
password_hash ($password, PASSWORD_DEFAULT);
Obviously, when a user logs in, I can take the opportunity to create the new hash from the password they just provided, and save that in the database.
However, I'd like to avoid having to have two fields in the database, namely one for the deprecated hash and one for the modern password_hash one. Instead, I'd rather just replace the old ones as each user logs in.
Therefore, is it possible to keep a single database field, and have the userland code determine whether the hash is old, i.e. determine which check to use?
(I'm assuming that the hash('sha512') hashes cannot be automatically upgraded to crypt() ones?)
Hashes created with password_hash will have a very distinctive $2y$ string at the beginning (or similar $..$, as long as you're operating with the current default Blowfish cypher), while SHA256 will simply be all hex values. Therefore, you can simply test whether a value is a legacy hash value or a password_hash value:
function isLegacyHash($hash) {
return !preg_match('/^\$\w{2}\$/', $hash);
}
Using this, you can keep both types of hashes in a single field and upgrade them when the user logs in. Alternatively, you could simply set a flag in a column like hash_version.
You will have to rehash when a user logs in. But there is a function to check already, see password_needs_rehash.
So when a user logs in, run the check and change the password hash if it needs a rehash.
It will be a little more tricky if you decide to completely migrate to bcrypt at some point. Then you will need to think about what to do with the users who have not had a new hash created.
The following code will return true so you know you need to rehash.
$password = 'test';
$oldHash = hash('sha512',); // get old Hash from DB
if (password_needs_rehash($oldHash, PASSWORD_BCRYPT)) {
$newHash = password_hash($password , PASSWORD_BCRYPT);
// save new Hash to DB (IMPORTANT: only if log in was successful...)
}

Password does not match after being encrypted using crypt() and password_hash() function

I modified my old post. I tried the crypt() function and now trying to work with password_hash() and password_verify() to verify the encrypted password coming from database but on each call, password_hash() function retuns a different encrypted string and password_verify() cannot match it.
This is how I am doing this.
//please ignore the syntax error if any
$data = '11';
$dbpass = password_hash($data, PASSWORD_BCRYPT);
echo $dbpass; // displays the random strings on each page refresh.
Once password is saved into database does not get match during the login process. Below is my actual function.
private function process_data($password){
$password = __STR.$password.__STR;
return password_hash($password, PASSWORD_BCRYPT);
}
private function processed($login_password, $dbpassword){
$login_password = __STR.$login_password.__STR;
return password_verify($login_password, $dbpassword);
}
On each function call for creating a hashed string for password, the function returns the different string next time.
Ok, Let's go through this one by one.
First, it's hashing, not encryption. Encryption is two-way, hashing is one way. We want to hash. We never want to encrypt. Yes, terminology matters. Please use the correct terminology.
Next, each call to password_hash is supposed to return a different hash. That's because it's generating a strong random salt. This is how it was designed, and how you really should be using it.
Further, DO NOT do the "pepper" thing of adding __STR before and after the password. You're doing nothing but potentially weakening the users password (which is not good). If you want more information around why that's a bad idea: Read This Answer.
Continuing, I would highly recommend that you do not use crypt directly. It is actually surprisingly easy to screw up and generate extremely weak hashes. This is why the password_* api was designed. crypt is a low level library, you want to use a high level library in your code. For more information on ways to screw up bcrypt, check out my blog: Seven Ways To Screw Up Bcrypt.
The Password API was designed to be a simple, one-stop shop. If it's not working for you check the following things:
Are you using PHP >= 5.5.0? Or are you using PHP >= 5.3.7 with password_compat?
Is your database column wide enough?
It needs to be at least 60 characters long.
Are you checking that the result of the function is a string, and not bool(false)?
If there is an internal error, it will return a non-string from password_hash.
Are you getting any errors?
Have you turned on error_reporting to its maximum setting (I recommend -1 to catch everything) and checked that the code isn't throwing any errors?
Are you sure you are using it correctly?
function saveUser($username, $password) {
$hash = password_hash($password, PASSWORD_BCRYPT);
// save $username and $hash to db
}
function login($username, $password) {
// fetch $hash from db
return password_verify($password, $hash);
}
Note that each one should be called only once.
Are you using PHP < 5.3.7 with password_compat? If so, this is your problem. You are using the compatability library on an unsupported version of PHP. You may get it to work (certain RedHat distributions have backported the necessary fixes), but you are using an unsupported version. Please upgrade to a reasonable release.
If all else fails, please try running this code and reporting back the output:
$hash = '$2y$04$usesomesillystringfore7hnbRJHxXVLeakoG8K30oukPsA.ztMG';
$test = crypt("password", $hash);
$pass = $test == $hash;
echo "Test for functionality of compat library: " . ($pass ? "Pass" : "Fail");
echo "\n";
If that returns Fail, you are running an unsupported version of PHP and should upgrade. If it returns pass, than the error is somewhere in your logic (the library is functioning fine).
The best way to store passwords is to use PHP's function password_hash(). It automatically generates a cryptographically safe salt for each password and includes it in the resulting 60-character string. You won't have to worry about the salt at all!
// Hash a new password for storing in the database.
// The function automatically generates a cryptographically safe salt.
$hashToStoreInDb = password_hash($password, PASSWORD_BCRYPT);
// Check if the hash of the entered login password, matches the stored hash.
// The salt and the cost factor will be extracted from $existingHashFromDb.
$isPasswordCorrect = password_verify($password, $existingHashFromDb);
Your own scheme is very weak, first you are using MD5 which is ways too fast for generating password hashes, then you use a static salt, which defeats the purpose of a salt. Maybe you want to have a look at my tutorial about safely storing passwords.
Edit to answer updated question:
It is not necessary to add the __STR to the password (if you want to add a pepper there are better ways), but your example functions should actually work. The returned value of password_hash() will be different each time because of the random salt. This is correct, the function password_verify() is able to extract this salt for the verification. In your case the database field is probably the problem. Make sure it can hold a 60 character string.

Categories