Security – Updating Password Hashing Without Forcing New Passwords

language-agnosticpasswordsSecurity

You maintain an existing application with an established user base. Over time it is decided that the current password hashing technique is outdated and needs to be upgraded. Furthermore, for UX reasons, you don't want existing users to be forced to update their password. The whole password hashing update needs to happen behind the screen.

Assume a 'simplistic' database model for users that contains:

  1. ID
  2. Email
  3. Password

How does one go around to solving such a requirement?


My current thoughts are:

  • create a new hashing method in the appropriate class
  • update the user table in the database to hold an additional password
    field
  • Once a user successfully logs in using the outdated password hash, fill the second password field with the updated hash

This leaves me with the problem that I cannot reasonable differentiate between users who have and those who have not updated their password hash and thus will be forced to check both. This seems horribly flawed.

Furthermore this basically means that the old hashing technique could be forced to stay indefinitely until every single user has updated their password. Only at that moment could I start removing the old hashing check and remove the superfluous database field.

I'm mainly looking for some design tips here, since my current 'solution' is dirty, incomplete and what not, but if actual code is required to describe a possible solution, feel free to use any language.

Best Answer

I would suggest adding a new field, "hash_method", with perhaps a 1 to signify the old method and a 2 to signify the new method.

Reasonably speaking, if you care about this sort of thing and your application is relatively long-lived (which it apparently already is), this is probably going to happen again as cryptography and information security is such an evolving, rather unpredictable field. There was a time when a simple run through MD5 was standard, if hashing was used at all! Then one might think they should use SHA1, and now there's salting, global salt + individual random salt, SHA3, different methods of crypto-ready random number generation...this isn't going to just 'stop', so you might as well fix this in an extensible, repeatable way.

So, lets say now you have something like (in pseudo-javascript for simplicity, I hope):

var user = getUserByID(id);
var tryPassword = hashPassword(getInputPassword());


if (user.getPasswordHash() == tryPassword)
{
    // Authenticated!
}

function hashPassword(clearPassword)
{
    // TODO: Learn what "hash" means
    return clearPassword + "H@$I-I";
}

Now realizing there is a better method, you just need to give a minor refactoring:

var user = getUserByID(id);
var tryPassword = hashPassword(getInputPassword(), user.getHashingMethod());

if (user.getPasswordHash() == tryPassword)
{
    // Authenticated!
}

function hashPassword(clearPassword, hashMethod)
{
    // Note: Hash doesn't mean what we thought it did. Oops...

    var hash;
    if (hashMethod == 1)
    {
        hash = clearPassword + "H@$I-I";
    }
    else if (hashMethod == 2)
    {
        // Totally gonna get it right this time.
        hash = SuperMethodTheNSASaidWasAwesome(clearPassword);
    }
    return hash;
}

No secret agents or programmers were harmed in the production of this answer.

Related Topic