1
votes

A former developer used the PHP hash() function with the SHA256 algorithm to store password hashes. To improve the security of the system, I'd like to start using crypt() with the Blowfish algorithm (unfortunately we don't have PHP 5.5 and thus password_hash() is not available).

Since SHA256 is a non-reversible hashing algorithm, is there a way to start using crypt() with the salted passwords without asking everyone to reset their password?

7
I tried to answer a similar question here Moving old passwords to new hashing algorithm?. You can use password_hash() with the compatibility pack.martinstoeckli

7 Answers

3
votes

You should use the compatibility library then. It will make it easier for you when you move to 5.5.

Re-hashing without asking the user for the password... well, you can wait until the next time users log in, and then use the password extension's password_verify() function. If it fails then you can fall back on the old SHA256 hash. If the SHA256 hash matches then you can rehash the password using password_hash() and save it in the old hash's place:

if (password_verify($password, $hash)) {
    // Matches...
} elseif (hash('sha256', $password) == $hash) {
    // Matches...
    $newHash = password_hash($password);
    // Save $newHash in the old hash's place
} else {
    die('Invalid password...');
}

It is technically possible to crack a lot of the hashes, but there are too many problems with that (you would not get all of them, it is most likely not feasible, it may not even be legal, etc.).

3
votes

Another approach you may want to consider is hash chaining: since you can't reverse SHA256, just define your new hash function as crypt(sha256($passwd)). Since you presumably already have sha256($passwd) on file for all your passwords, it's possible to crypt() each one of them with an appropriate salt to update your existing hashes (without having to wait for the user to log in).

1
votes

Here's a possible solution:

Add a column to the user table to indicate which hashing method has been used on the user's password. At login time you'll know the user's password since he's just entered it, so once the current hash has been passed, crate a new has from the password and update the flag column

This assumes that you're passing free text passwords across the internet which you shouldn't do unless you're using SSL.

Alternatively, if you're hashing the password on the client before sending it, update the client software to handle two hashing algorithms and send both. Use your flag to identify which to check.

In both cases once all (or the majority of) your users have switched, deleted the old hashes and force the issue for the remaining users.

0
votes

I'm assuming you never stored the clear-text user account passwords, as this would be truly an awful thing to do. SO therefore you no longer have the data that you need to create the new password digests.

I'm thinking you're going to need everyone to update their passwords.

0
votes

There may be other faster and more efficient ways to do this, but if you want to do this without it affecting your users, this is how I would do it -

Add another column to your table, a basic flag that can go True or False. Default it to false. Then implement the following pseudocode :

if(flag=true)
{
 use crypt() and authenticate user
}
else
{
use hash() and authenticate user
use crypt() on the provided password (once authenticated)
update the record to put the new password into the table
set flag=true
}

Essentially it check if the password is updated or not, and updates it if it isn't. You can eventually take this function off, once your users have made the transition. But as it adds almost no load, I would recommend keeping it!

Its a bit roundabout, but it will have the minimal amount of work for your users to do, and it will run in the background without giving them any indication of it happening!

0
votes

Id say use a marker field to annotate which users have been previously hashed, then crypt the existing hash. For anyone with a true in the marker field when authenticating it becomes a 2 step process, hash the password then crypt it for a match check.

Whenever they update their password you would set the marker field to false. i.e. phase in and just crypt for a match check.

0
votes

Yes, what you will need to do is add an extra column that stores the crypt() output. When a user logs in, and their password successfully hash()es to what you have in the database, you can now crypt() that password, and remove the old hash from the database.

This only works when users log-in, so you will have a period of time where some users are using the old system, and some users are using the new one.