Search code examples
pythonasp.netrubyumbraco

Implement an Umbraco/ASP.NET password validation script in Ruby / Python


I have exported about 1K users from an Umbraco database. We have emails and hashed_passwords for each. There appears to be no per-user salt.

Umbraco project version is 7.4 with the following (default) settings:

<membership defaultProvider="UmbracoMembershipProvider" userIsOnlineTimeWindow="15">
  <providers>
    <clear />
    <add name="UmbracoMembershipProvider" type="Umbraco.Web.Security.Providers.MembersMembershipProvider, Umbraco.Web" minRequiredNonalphanumericCharacters="0" minRequiredPasswordLength="5" useLegacyEncoding="false" enablePasswordRetrieval="false" enablePasswordReset="false" requiresQuestionAndAnswer="false" defaultMemberTypeAlias="Member" passwordFormat="Hashed" allowManuallyChangingPassword="true" />
    <add name="UsersMembershipProvider" type="Umbraco.Web.Security.Providers.UsersMembershipProvider, Umbraco.Web" />
  </providers>
</membership>
<!-- Role Provider -->
<roleManager enabled="true" defaultProvider="UmbracoRoleProvider">
  <providers>
    <clear />
    <add name="UmbracoRoleProvider" type="Umbraco.Web.Security.Providers.MembersRoleProvider" />
  </providers>
</roleManager>
<machineKey validationKey="9FEB5*******************B7348B27A6C" decryptionKey="73934**************69366" validation="HMACSHA256" decryption="AES" />

Asp.net code used seems to be this one:

https://referencesource.microsoft.com/#System.Web/Security/SQLMembershipProvider.cs,f37d42faca2b921e,references

I have setup this code in Ruby to properly encode the plain text password and compare it with the hashed passwords, so that users can login without having to reset their original plain text passwords:

def encode_password(password, salt)

    require "base64"
    require "digest"

    bytes = ""
    password.each_char { |c| bytes += c + "\x00" }
    salty = Base64.decode64(salt)
    concat = salty+bytes
    sha256 = Digest::SHA256.digest(concat)
    encoded = Base64.encode64(sha256).strip()
    puts encoded
  end

But my problem is that I don't know which salt to use. From what I've been able to research here:

https://shazwazza.com/post/umbraco-passwords-and-aspnet-machine-keys

Umbraco uses a single salt derived from the machineKey, but it's unclear which part exactly. And it's unclear if it should be part of the validationKey or the decryptionKey


Solution

  • the salt is not based on the machine key. The blog post says:

    The key used to hash the passwords is the generated salt we produce it is not the key part of the Machine Key

    and

    The part of the Machine Key that is used to hash the passwords is specifically the algorithm type.

    So the only part of the machine key that is used is the algorithm type.

    This method EncryptOrHashNewPassword is what creates the salt + password. It then calls EncryptOrHashPassword with the provided salt to do the password hashing. All of that is called from this method HashPasswordForStorage which takes the fully hashed password along with the salt used to hash the password and stores both of them together as a string (base64 iirc).

    When verifying the password, the salt is parsed from the stored string and is used to hash the incoming password to see if they match. This unit test somewhat shows this

    That's pretty much what you would need to port to Ruby/Python.