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

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...)
}

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.

Decrypt password in php

How to decrypt password in plain text which are in ms-SQL database?
$encrypted_password="k??aU?????y-??N???tDRz????{?4R???G?aS4t?T";
$salt = "611233880";
So I need to decrypt password so that I insert into other database with md5 encryption.
I used this code, but not get success
$iv2 = '';
for($i=0;$i<16;$i++){
$iv2 .= "\0";
}
$plain_text_CBC = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $salt, $encrypted_password, MCRYPT_MODE_CBC, $iv2);
var_dump($plain_text_CBC);
$plaintext = openssl_decrypt($encrypted_password, 'AES-256-CBC', $salt, 0, $iv2);
var_dump($plaintext);
Need Help
The idea behind encrypted (or hashed) passwords is that it is a one way operation. Not quite like shredding, but that's the idea. If you take exactly the same input and shred it you should get exactly the same output. You may not be able to reconstruct the input from it, but you can confirm someone gave you the right input by looking at the output.
Some weak algorithms have been know to be hacked buy in principle what you are asking for is impossible.
The ought to be no reason reason to decrypt. You can always do the hashing operation twice - first with the old algorithm, then with the new one - and then compare with the entry in the database.
NEVER EVER store plaintext (or weakly encrypted) passwords. Just ask LinkedIn...
You don't simply decrypt a password. It should be hashed which means it is a one way encryption.
If you want to change your password hashing implementation, here is a way to do it.
You have the clear text password available when a user is in the process of logging in. So that's where you will have to place code to rehash the password with the new algorithm.
If you are using the new native password hashing functions (PHP Version >= 5.5) then you can use password_needs_rehash. If you are on a lower PHP Version but still >= 5.3.7 then you can use the userland implementation to get the same API to the password hashing functions.
So when a user is attempting to log in and the password needs rehashing, check if the hashes match with the old hashing function and then create and save the new one to the database. Over time you will be able to migrate most users and then you can think about a solution to migrate the rest of your userbase with a forced password reset if they never logged in during your migration timeframe.
Firstly, you encrypting your data by 2 different algorithms. Why? One algorithm is enough.
Answer: You can't decrypt old password.
Solution: You should encrypt data you wrote into password field and compare result with data in database. If they are equal, you will pass password check.
For example:
$login = mysqli_real_escape_string($_POST['login']);
$password = mysqli_real_escape_string($_POST['password']);
$password_hash = md5($input); // you can use there any other algorithm, just example
// make next query and control result
$sql = 'select count(id) from users where login = \'$login\' and password = \'$password_hash\'';
// now if there are 1 row with this login and same password hash let user log in to your site
If you write your code in the MVC structure, you can use the function n_decrypt() to decrypt passwords.

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.

How can I store my users' passwords safely?

How much more safe is this than plain MD5? I've just started looking into password security. I'm pretty new to PHP.
$salt = 'csdnfgksdgojnmfnb';
$password = md5($salt.$_POST['password']);
$result = mysql_query("SELECT id FROM users
WHERE username = '".mysql_real_escape_string($_POST['username'])."'
AND password = '$password'");
if (mysql_num_rows($result) < 1) {
/* Access denied */
echo "The username or password you entered is incorrect.";
}
else {
$_SESSION['id'] = mysql_result($result, 0, 'id');
#header("Location: ./");
echo "Hello $_SESSION[id]!";
}
The easiest way to get your password storage scheme secure is by using a standard library.
Because security tends to be a lot more complicated and with more invisible screw up possibilities than most programmers could tackle alone, using a standard library is almost always easiest and most secure (if not the only) available option.
The new PHP password API (5.5.0+)
If you are using PHP version 5.5.0 or newer, you can use the new simplified password hashing API
Example of code using PHP's password API:
<?php
// $hash is what you would store in your database
$hash = password_hash($_POST['password'], PASSWORD_DEFAULT, ['cost' => 12]);
// $hash would be the $hash (above) stored in your database for this user
$checked = password_verify($_POST['password'], $hash);
if ($checked) {
echo 'password correct';
} else {
echo 'wrong credentials';
}
(In case you are still using legacy 5.3.7 or newer you can install ircmaxell/password_compat to have access to the build-in functions)
Improving upon salted hashes: add pepper
If you want extra security, the security folks now (2017) recommend adding a 'pepper' to the (automatically) salted password hashes.
There is a simple, drop in class that securely implements this pattern, I recommend:
Netsilik/PepperedPasswords
(github).
It comes with a MIT License, so you can use it however you want, even in proprietary projects.
Example of code using Netsilik/PepperedPasswords:
<?php
use Netsilik/Lib/PepperedPasswords;
// Some long, random, binary string, encoded as hexadecimal; stored in your configuration (NOT in your Database, as that would defeat the entire purpose of the pepper).
$config['pepper'] = hex2bin('012345679ABCDEF012345679ABCDEF012345679ABCDEF012345679ABCDEF');
$hasher = new PepperedPasswords($config['pepper']);
// $hash is what you would store in your database
$hash = $hasher->hash($_POST['password']);
// $hash would be the $hash (above) stored in your database for this user
$checked = $hasher->verify($_POST['password'], $hash);
if ($checked) {
echo 'password correct';
} else {
echo 'wrong credentials';
}
The OLD standard library
Please note: you should not be needing this anymore! This is only here for historical purposes.
Take a look at: Portable PHP password hashing framework: phpass and make sure you use the CRYPT_BLOWFISH algorithm if at all possible.
Example of code using phpass (v0.2):
<?php
require('PasswordHash.php');
$pwdHasher = new PasswordHash(8, FALSE);
// $hash is what you would store in your database
$hash = $pwdHasher->HashPassword( $password );
// $hash would be the $hash (above) stored in your database for this user
$checked = $pwdHasher->CheckPassword($password, $hash);
if ($checked) {
echo 'password correct';
} else {
echo 'wrong credentials';
}
PHPass has been implemented in some quite well known projects:
phpBB3
WordPress 2.5+ as well as bbPress
the Drupal 7 release, (module available for Drupal 5 & 6)
others
The good thing is that you do not need to worry about the details, those details have been programmed by people with experience and reviewed by many folks on the internet.
For more information on password storage schemes, read Jeff`s blog post: You're Probably Storing Passwords Incorrectly
Whatever you do if you go for the 'I'll do it myself, thank you' approach, do not use MD5 or SHA1 anymore. They are nice hashing algorithm, but considered broken for security purposes.
Currently, using crypt, with CRYPT_BLOWFISH is the best practice.
CRYPT_BLOWFISH in PHP is an implementation of the Bcrypt hash. Bcrypt is based on the Blowfish block cipher, making use of it's expensive key setup to slow the algorithm down.
Your users will be much safer if you used parameterized queries instead of concatenating SQL statements. And the salt should be unique for each user and should be stored along with the password hash.
A better way would be for each user to have a unique salt.
The benefit of having a salt is that it makes it harder for an attacker to pre-generate the MD5 signature of every dictionary word. But if an attacker learns that you have a fixed salt, they could then pre-generate the MD5 signature of every dictionary word prefixed by your fixed salt.
A better way is each time a user changes their password, your system generate a random salt and store that salt along with the user record. It makes it a bit more expensive to check the password (since you need to look up the salt before you can generate the MD5 signature) but it makes it much more difficult for an attacker to pre-generate MD5's.
With PHP 5.5 (what I describe is available to even earlier versions, see below) around the corner I'd like to suggest to use its new, built-in solution: password_hash() and password_verify(). It provides several options in order to achieve the level of password security you need (for example by specifying a "cost" parameter through the $options array)
<?php
var_dump(password_hash("my-secret-password", PASSWORD_DEFAULT));
$options = array(
'cost' => 7, // this is the number of rounds for bcrypt
// 'salt' => 'TphfsM82o1uEKlfP9vf1f', // you could specify a salt but it is not recommended
);
var_dump(password_hash("my-secret-password", PASSWORD_BCRYPT, $options));
?>
will return
string(60) "$2y$10$w2LxXdIcqJpD6idFTNn.eeZbKesdu5y41ksL22iI8C4/6EweI7OK."
string(60) "$2y$07$TphfsM82o1uEKlfP9vf1fOKohBqGVXOJEmnUtQu7Y1UMft1R4D3d."
As you might see, the string contains the salt as well as the cost that was specified in the options. It also contains the algorithm used.
Therefore, when checking the password (for example when the user logs in), when using the complimentary password_verify() function it will extract the necessary crypto parameters from the password hash itself.
When not specifying a salt, the generated password hash will be different upon every call of password_hash() because the salt is generated randomly. Therefore comparing a previous hash with a newly generated one will fail, even for a correct password.
Verifying works like this:
var_dump(password_verify("my-secret-password", '$2y$10$BjHJbMCNWIJq7xiAeyFaHOGaO0jjNoE11e0YAer6Zu01OZHN/gk6K'));
var_dump(password_verify("wrong-password", '$2y$10$BjHJbMCNWIJq7xiAeyFaHOGaO0jjNoE11e0YAer6Zu01OZHN/gk6K'));
var_dump(password_verify("my-secret-password", '$2y$07$TphfsM82o1uEKlfP9vf1fOKohBqGVXOJEmnUtQu7Y1UMft1R4D3d.'));
var_dump(password_verify("wrong-password", '$2y$07$TphfsM82o1uEKlfP9vf1fOKohBqGVXOJEmnUtQu7Y1UMft1R4D3d.'));
I hope that providing these built-in functions will soon provide better password security in case of data theft, as it reduces the amount of thought the programmer has to put into a proper implementation.
There is a small library (one PHP file) that will give you PHP 5.5's password_hash in PHP 5.3.7+: https://github.com/ircmaxell/password_compat
That's fine with me. Mr Atwood wrote about the strength of MD5 against rainbow tables, and basically with a long salt like that you're sitting pretty (though some random punctuation/numbers, it could improve it).
You could also look at SHA-1, which seems to be getting more popular these days.
I want to add:
Don't limit users passwords by length
For compatibility with old systems often set a limit for the maximum length of the password. This is a bad security policy: if you set restriction, set it only for the minimum length of passwords.
Don't send user passwords via email
For recovering a forgotten password you should send the address by which user can change the password.
Update the hashes of users passwords
The password hash may be out of date (parameters of the algorithm may be updated). By using the function password_needs_rehash() you can check it out.
Here's a PHP + CouchDB.apache.org login system that doesn't store plaintext passwords.
According to the advice that I've read, it should be totally secure.
CMS login code : https://github.com/nicerapp/nicerapp/blob/24ff0ca317b28c1d91aee66041320976a6d76da7/nicerapp/boot.php#L56
calls
https://github.com/nicerapp/nicerapp/blob/24ff0ca317b28c1d91aee66041320976a6d76da7/nicerapp/functions.php#L171
app(s) specific business code :
https://github.com/nicerapp/nicerapp/blob/24ff0ca317b28c1d91aee66041320976a6d76da7/nicerapp/ajax_login.php#L87
calls
https://github.com/nicerapp/nicerapp/blob/24ff0ca317b28c1d91aee66041320976a6d76da7/nicerapp/functions.php#L230
which in turn calls :
https://github.com/nicerapp/nicerapp/blob/2d479b3e22dce9e7073525481b775f1bf7389634/nicerapp/apps/nicer.app/webmail/recrypt.php#L2
and to edit the webmail app config data into the database :
https://github.com/nicerapp/nicerapp/blob/main/nicerapp/apps/nicer.app/webmail/ajax_editConfig.php

Importing MD5+Salt Passwords to MD5

I'm moving my site from an oscommerce store to a commercial application.
The new application stores its passwords using straight MD5 encryption. Oscommerce stores the password using MD5, but also adds a random 2 digit number (provided in plaintext) to the hash.
Here is what someone posted on a forum:
The two characters added are for creating the hash in such way that
hash=md5(twocharactersPlainPassword)
ie: 2letters: 74
Plain Password: PaSs
hash=md5('74PaSs')=acaa6e689ae0008285320e6617ca8e95:74
Here is the code how Oscommerce encrypts the password:
// This function makes a new password from a plaintext password.
function tep_encrypt_password($plain) {
$password = '';
for ($i=0; $i<10; $i++) {
$password .= tep_rand();
}
$salt = substr(md5($password), 0, 2);
$password = md5($salt . $plain) . ':' . $salt;
return $password;
}
// This funstion validates a plain text password with an encrypted password
function tep_validate_password($plain, $encrypted) {
if (tep_not_null($plain) && tep_not_null($encrypted)) {
// split apart the hash / salt
$stack = explode(':', $encrypted);
if (sizeof($stack) != 2) {
return false;
}
if (md5($stack[1] . $plain) == $stack[0]) {
return true;
}
}
return false;
}
Here is how my new cart encrypts the password:
if ($admin_password_encrypt == 1) {
$password_match = md5($password);
} else {
$password_match = $password;
}
Is there any possible way of importing customer passwords from my oscommerce cart to my new cart.
Do not save plain MD5 hashes in your database. Plain MD5 hashes can be reverse engineered quickly and easily using rainbow tables. However, here's how you solve your problem, no matter how you choose to store the passwords in the future:
Create a column in your new database that specifies the "version" of the password. This is used to determine if the password was generated by the old application or the new one.
Import the old users, setting the aforementioned flag to indicate the password is imported.
Create two methods for validating a password. One method uses the code from your old application, the other uses your new validation method.
When a user is logging in, check the aforementioned flag and use the appropriate validation method.
Anyways, I want to reiterate that plain MD5 hashes are easy to crack for most passwords (since people like short and easy to remember passwords.) Use a salt and/or a more complex algorithm. I'd recommend both, and use a salt that is longer than two characters and not limited to numbers. This will make the passwords really secure.
It appears that you have the source code for your new cart. Since "straight MD5" is a terribly awful way of storing passwords, perhaps you should simply change the to use the same password storage mechanism as OSCommerce.
The answer to your question is no, there is no way of converting the passwords.
No. MD5 is a hash algorithm, which is a one-way function. You cannot reverse the hash on your oscommerce system to remove the salt and rehash. Sorry.
If the passwords are encrypted with md5, you won't be able to decrypt them. Your best possibility can be to check in your login code whether the creation of an account/last password change occurred before a certain date. If so, use OSCommerce's password validation function, if not, use your own.
This way, for all new accounts the passwords will be encrypted with the new method, and for old accounts you'd continue to handle them as usual, so it'll be transparent to users.
Another, and possibly better option is that you continue to use the salting method of OsCommerce. It is more secure, and you'll also get to keep your existing passwords.
There is no method for automatic conversion between hash algorithms. Unfortunately you would likely be stuck picking from one of the following bad options:
Configure or program old cart to store hashes in new format as users login to old system.
Use a password cracker to recover some percentage of old system cart passwords.
Ask new vendor to support old format
Send notification to all users they will need to prepend the salt text to their passwords when using the new system or customize the system to prepend known salts for them.

Categories