v5.02 Password Hashing implementation not rehashing legacy passwords

I currently employ an OrmLiteAuthRepository populated with UserAuth records that include Salt, PasswordHash and DigestHa1Hash values.

Having recently deployed v5.02 with an existing database none of the users can successfully authenticate. The error is Invalid UserName or Password. Although not initially, I have then enabled UseSaltedHash in HostConfig and set CreateDigestAuthHashes in AuthFeature but after each I still could not authenticate.

If I reset the password I can get a session but I cannot do this for every user in production. The new hashing method is not a problem but as I cannot authenticate the passwords are not being rehashed;

both new users and successful authentication logins where their password will automatically be re-hashed with the new implementation.

How can I work out why the current passwords are not accepted and the rehashing is failing?

If you’ve set UseSaltedHash = true then it should only be using the existing SaltedHash Password hashing implementation and should authenticate as before, but will not rehash existing passwords.

Otherwise by default it should let you authenticate with the old password and rehash.

We’ve got tests for both of these behavior in PasswordHasherTests.cs.

Can you provide an example of an old Password and its Hash + Salt that is not being authenticated?

Thanks, I’ve created a Self Hosted App which demonstrates the issue; https://github.com/davecanderson/ServiceStack-PasswordHashingIssue-5190.

Authenitcating with new@email.com/123456 succeeds but old@email.com/123456 does not. I confirmed this with Postman, I haven’t created actual unit tests for this yet.

Here are the failing values which are also included in password-reset-123456.sql within the repos;

`PasswordHash` = 'lmXQNKAoWc+HXADy3U2yLd0lzactUDB1OqlUtd4PeugDJ06oKdVID82ellAbnShUPGhDibDW+SWU82sb702DqA==', 
`DigestHa1Hash` = '94feafa0f52ba152208153d869c9fcbd', 
`Salt` = 'aQWJVmQ='

How did you create the password hash + salt as this unit test suggests it doesn’t match?

public void Verify_Password_Hash()
    var passwordHash = "lmXQNKAoWc+HXADy3U2yLd0lzactUDB1OqlUtd4PeugDJ06oKdVID82ellAbnShUPGhDibDW+SWU82sb702DqA==";
    var salt = "aQWJVmQ=";

    var isMatch = new SaltedHash().VerifyHashString("123456", passwordHash, salt);

Are you using a custom IHashProvider implementation?

I got that hash from our production database where I know that still works. Should it work when copying the values between databases? We use SQLite for our developer instances and MySQL for deployments. We drop and recreate the schema for dev builds which is why everything was working until now. The deployment against the existing database we just run an update script to apply any schema changes.

I’ve not configured any Hash Provider but we do have several other AuthProviders enabled. This is affecting all the userauth records so I’m pretty sure it’s not a forgotten password problem.

I must be missing something about how the password hashing works. I’ll probably roll-back to the production version of our code base to see if the problem still exists.

The implementation for creating and verifying password hashes in SaltedHash hasn’t changed so it should continue to work as before.

The Hash and Salt are Base64 strings so there shouldn’t be any issue with copying the same values between databases. In this case the reason it’s not working is because the hash just isn’t valid with the password "123456".

There’s no issue with SaltedHash implementation in the latest version which continues to work as before:

public void Generate_and_Verify_Password_Hash()
    var password = "123456";
    new SaltedHash().GetHashAndSaltString(password, out string passwordHash, out string salt);

    Console.WriteLine($$"Password: {password}, Hash: {passwordHash}, Salt: {salt}");

    var isMatch = new SaltedHash().VerifyHashString("123456", passwordHash, salt);

Where the test runs successfully and prints:

Password: 123456, Hash: P7qMsiHO4LAwy5RPe4X0Bbn6QtpSSmgf4I8t1l0wzo4=, Salt: gycMCQ==

Sorry, yes we do register a custom IHashProvider that I hadn’t noticed was configured;

container.Register<IHashProvider>(c => new SaltedHash(new SHA512Managed(), 5));

This looks like it should be resolved in AuthExtensions.VerifyPassword;

var oldSaltedHashProvider = HostContext.Resolve<IHashProvider>();

From within our application I can see the resolved instance still uses SHA256Managed as the hash algorithm. I assume I’ve not registered the IHashProvider correctly? That’s a bit confusing though because it must have resolved at some point to have created the SHA512 hash in the first place.

If you’re using a custom IHashProvider you need to set UseSaltedHash = true to tell ServiceStack to use the old IHashProvider provider. From v5 ServiceStack uses the new IPasswordHasher interface by default.

If you’re using a Custom Auth Repository it will also need to be updated to use the new password verification APIs, please refer to OrmLiteAuthRepository for a complete concrete example.

Our underlying issue is actually the way we’ve registered the IHashProvider with Autofac.ContainerBuilder. We have also configured FunqControllerFactory so that MVC uses the same IoC as ServiceStack. When I explicitly register IHashProvider with Funq.Container everything works as expected. I can even omit UseSaltedHash = true and the password is upgraded to the new PBKDF2 format.

Thanks for all your assistance, time to understand the mess we’ve made of IoC.

1 Like