Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ASP.NET membership HashAlgorithmType defaults to HMACSHA256, so is password hash keyed?

I have an ASP.NET 4.5 web application that uses a SqlMembershipProvider. During development, someone put passwordFormat="Clear" in the config causing the passwords to be saved in clear text. I want to remove this and enable hashing of passwords, but I want to make sure the hashes are not being generated using a machine-specific, or auto-generated key.

According to what I read on all the related Q&A, the passwords are simply hashed using straight SHA256 and are not machine-specific or keyed. However, when I check the Membership.HashAlgorithmType at runtime, its value is "HMACSHA256" and the .NET HMACSHA256 class requires a key (because it's HMAC) and randomly generates one if one is not provided to the constructor. So it seems there must be a key involved. So is the Membership password hash keyed or not? If it is, how do I use the same key across machines? Documentation or evidence supporting your answer will be appreciated.

Edit

According to this MSDN page on "Securing Membership", the machineKey element does control the hash key:

It is highly recommended that you encrypt user passwords in the membership data source using a passwordFormat attribute set to Hashed or Encrypted, where Hashed is the most secure format. The encryption key values for the specified encryption algorithm are stored in the machineKey configuration element.

However, I tried setting the machine key validation key in the web.config and it still generated the same password hash as before it was set so it seems to have no effect.

I have also been looking at the SqlMembershipProvider source code and as far as I can tell, it does use a keyed hash. Granted that is the source for .Net 4.6.1 and I am running 4.5. I've copied the EncodePassword source code and modified it so it can run in a console app and in my console app I still get a different hash than the MembershipProvider result (on the same machine) for the same password and salt value.

If it was using a random key, you wouldn't be able to validate passwords. So where does the SqlMembershipProvider get it's hash key from??

like image 567
xr280xr Avatar asked Dec 14 '15 17:12

xr280xr


1 Answers

After more digging around in the source code and online, I found a question on SO where the user noticed his/her SQLMembershipProvider hashes were only 20 bytes in the database. They should be 64. I checked mine and they were too. I tried to determine how a SHA256 hash could end up as only 20 bytes and came to the conclusion it can't. So in my console app, I tried using just SHA1 just for the hell of it. I compared the result of that to a hash created by the SQLMembershipProvider and sure enough, it matched! My SQLMembershipProvider was using SHA1 even though Membership.HashAlgorithmType indicates it is set to the .NET 4.0 default, "HMACSHA256".

This is where my reading the source code came in handy. I remembered there's a conditional section that I removed from my console app that checks for some legacy mode when creating the hash algorithm class.

        private HashAlgorithm GetHashAlgorithm() {
        if (s_HashAlgorithm != null)
            return HashAlgorithm.Create(s_HashAlgorithm);

        string temp = Membership.HashAlgorithmType;
        if (_LegacyPasswordCompatibilityMode == MembershipPasswordCompatibilityMode.Framework20 &&
            !Membership.IsHashAlgorithmFromMembershipConfig &&
            temp != "MD5")
        {
            temp = "SHA1";
        }
        HashAlgorithm hashAlgo = HashAlgorithm.Create(temp);
        if (hashAlgo == null)
            RuntimeConfig.GetAppConfig().Membership.ThrowHashAlgorithmException();
        s_HashAlgorithm = temp;
        return hashAlgo;
    }

After some more digging I determined _LegacyPasswordCompatibilityMode gets set from a config attribute named passwordCompatMode and this belongs to the SqlMembershipProvider, even though it doesn't show up in intellisense in the web.config. Once I explicitly set passwordCompatMode="Framework40" on the SqlMembershipProvider in my web.config, my hash started using a keyed HMACSHA256 hash where the key was the machineKey's validationKey. Using the same validation key, I was then able to reproduce the same hashes from my console app.

As confirmation of the problem, when I remove the passwordCompatMode="Framework40" attribute, then in the debugger inspect an instance of the SqlMembershipProvider, I can see:

_LegacyPasswordCompatibilityMode = "Framework20"
s_HashAlgorithm = "SHA1" (!!!)

So here's the crazy thing. The MSDN doc on SqlMembershipProvider states:

The default value [for passwordCompatMode] is Framework20.

So unless you explicitly declare this obscure attribute as Framework40, the SqlMembershipProvider uses SHA1 despite the 4.0 Framework being advertised as upgrading to to HMACSHA256. It was hard enough to find documentation inferring the key comes from the machineKey section ... hell it was hard to even find that 4.0 uses HMACSHA256, not just SHA256 ... but I didn't see anywhere else stating this attribute is required. I wonder how many developers are under the impression they're using a more secure HMACSHA[X] when they are actually still only using SHA1? Hope this helps people.

Edit

The above is true if the <membership> element does not explicitly declare the hashAlgorithType. You can see in the .NET source code above that it looks for !Membership.IsHashAlgorithmFromMembershipConfig which indicates whether it found the hashAlgorithmType attribute on the membership element. So if you specify <membership ... hashAlgorithmType=[x]> in your config, I believe it doesn't need the passwordCompatMode attribute on the SqlMembershipProvider.

like image 119
xr280xr Avatar answered Nov 18 '22 17:11

xr280xr