Upgrading the Password Hashing Algorithm for Sitecore 9+ Installs

Upgrading the Password Hashing Algorithm for Sitecore 9+ Installs

The Importance of Security

Maintaining security is critical. Take a look at any of the major data breaches that have occurred in the past few years where millions of Americans have had their data compromised, and you might start to agree. One of the simplest measures we can take to protect our information is through the use of passwords. It makes sense that we put locks on our doors to protect our physical valuables, and the same can be said for our digital possessions as well. But locks can be picked, bypassed, and broken by those with malicious intent, and passwords are no different. That being said, just as you can buy a stronger lock, so too can you “create” a stronger password using hashing algorithms.

Sitecore and Password Hashing

Sitecore out-of-the-box comes with some decent password security in place in the form of password hashing. For those unaware, hashing will take your plaintext password and run it through a cryptographic algorithm to generate an alphanumerically random key that is functionally immune to password cracking techniques. The algorithms themselves are akin to the locks mentioned earlier: some are stronger, some are longer, and some are more complex. In version 9 of the CMS we all know and love, passwords are hashed using SHA1 by default. This is NOT an acceptable algorithm to use, and Sitecore’s own Security Hardening Guidelines state that you should upgrade this algorithm to SHA512 (also written as SHA2_512) to better guarantee the cryptographic security of your password(s).

This is easily accomplished by changing the hashAlgorithmType in the website’s root Web.config, either through direct modification or using transforms. By setting the node to SHA512 per the guidelines, you can all but ensure the cryptography of any future passwords will remain secure. Note that this applies to previous versions of Sitecore as well.

Scenario

So say you have made the switch and go to log into your instance using good ole “admin” : “b” as your username and password combo. But… you can’t log in? Why does this tried-and-true password combination no longer work? The answer is simple: when Sitecore was installed using the stock configuration, the default admin password was set using SHA1, but the Web.config was changed so that the login service would look for a password that has been hashed using SHA512. Because the two do not match, the password effectively no longer works and must be reset. Now, resetting the password is an easy task, one that Andy Burns details in this post. But say that you don’t like extra steps and want this all to be taken care of during the install process? The good news is that this is entirely possible with a little bit of tweaking.

The How-To

For the purposes of this post, we will be focusing on one of Sitecore 9’s shiny new features: the Sitecore Install Framework (SIF). This framework has effectively scripted the majority of the install process, requiring that you only set a few parameters and populate a few folders with the web deploy packages that Sitecore provides. On the topic of web deploy packages, let’s take a dive into the supplied archive and see where the hashing algorithm comes into play.

Local archive instance.

A local archive with the necessary files to use the Sitecore Install Framework.

Pictured above is the archive that I have on my local machine, where we see three archives contained within: the Sitecore instance, the xConnect instance, and some configuration files contained in a third zip. Let’s look at the Sitecore instance itself.

Stock Sitecore 9 web deploy package root

This screenshot shows that I have already modified the SQL script.

This archive contains several scripts and files, and the one we’ll focus on first is highlighted in the picture above: SetSitecoreAdminPassword.sql. When Sitecore is being installed, there comes a step where the script will take parameters that you have set and pass those to this SQL file for execution against the Core database. Here is the entirety of the script in its stock form:

declare @ApplicationName nvarchar(256) = 'sitecore'
declare @UserName nvarchar(256) = 'sitecore\admin'
declare @Password nvarchar(128) = 'PlaceHolderForPassword'
declare @HashAlgorithm nvarchar(10) = 'SHA1'
declare @PasswordFormat int = 1 -- Hashed
declare @CurrentTimeUtc datetime = SYSUTCDATETIME()
declare @Salt varbinary(16) = 0x
declare @HashedPassword varbinary(20)
declare @EncodedHash nvarchar(128)
declare @EncodedSalt nvarchar(128)

-- Generate random salt
while len(@Salt) < 16
begin
set @Salt = (@Salt + cast(cast(floor(rand() * 256) as tinyint) as binary(1)))
end

-- Hash password
set @HashedPassword = HASHBYTES(@HashAlgorithm, @Salt + cast(@Password as varbinary(128)));

-- Convert hash and salt to BASE64
select @EncodedHash = cast(N'' as xml).value(
'xs:base64Binary(xs:hexBinary(sql:column("bin")))'
, 'varchar(max)'
) from (select @HashedPassword as [bin] ) T

select @EncodedSalt = cast(N'' as xml).value(
'xs:base64Binary(xs:hexBinary(sql:column("bin")))'
, 'VARCHAR(MAX)'
) from (select @Salt as [bin] ) T

execute [dbo].[aspnet_Membership_SetPassword]
@ApplicationName
,@UserName
,@EncodedHash
,@EncodedSalt
,@CurrentTimeUtc
,@PasswordFormat

At first glance, it seems obvious what needs to be changed. The @HashAlgorithm variable on line 4 is the clear candidate. This gets passed into HASHBYTES, which adds a salt and then gets converted to base 64 before it is stored in the Core database. So by changing the variable to ‘SHA2_512’ we should be able to ensure that the admin password gets encoded correctly and be done with it. Well after poring over SQL tables and error logs, it was discovered that it was not so simple. Line 8 is also key because, by default, it sets the length to be 20 bits. This is fine for SHA1, but SHA512 has a length of 512 bits, meaning that any password encoded using this algorithm would be truncated and rendered useless to a login service expecting a 512-bit hashed password. So we must also change line 8 to declare @HashedPassword varbinary(512) in order to match the length of the algorithm’s output. The correct file is given below in its entirety.

declare @ApplicationName nvarchar(256) = 'sitecore'
declare @UserName nvarchar(256) = 'sitecore\admin'
declare @Password nvarchar(128) = 'PlaceHolderForPassword'
declare @HashAlgorithm nvarchar(10) = 'SHA2_512'
declare @PasswordFormat int = 1 -- Hashed
declare @CurrentTimeUtc datetime = SYSUTCDATETIME()
declare @Salt varbinary(16) = 0x
declare @HashedPassword varbinary(512)
declare @EncodedHash nvarchar(128)
declare @EncodedSalt nvarchar(128)

-- Generate random salt
while len(@Salt) < 16
begin
set @Salt = (@Salt + cast(cast(floor(rand() * 256) as tinyint) as binary(1)))
end

-- Hash password
set @HashedPassword = HASHBYTES(@HashAlgorithm, @Salt + cast(@Password as varbinary(128)));

-- Convert hash and salt to BASE64
select @EncodedHash = cast(N'' as xml).value(
'xs:base64Binary(xs:hexBinary(sql:column("bin")))'
, 'varchar(max)'
) from (select @HashedPassword as [bin] ) T

select @EncodedSalt = cast(N'' as xml).value(
'xs:base64Binary(xs:hexBinary(sql:column("bin")))'
, 'VARCHAR(MAX)'
) from (select @Salt as [bin] ) T

execute [dbo].[aspnet_Membership_SetPassword]
@ApplicationName
,@UserName
,@EncodedHash
,@EncodedSalt
,@CurrentTimeUtc
,@PasswordFormat

This will make sure that the administrator’s password is encoded using SHA512 and that “admin” : “b” is good to use once more. But we’re not done yet.

Stock Sitecore website root.

This screenshot shows that I have already updated the Web.config file.

Returning to the root of the Sitecore instance, we now need to make one last modification. Delving into the Content folder shows a fairly standard website root folder, replete with its own Web.config as shown in the picture above. You may have already guessed, but just as we needed to retroactively change the configuration to use the updated hashing algorithm, we must now modify the stock Web.config as well. The process is the same: change the hashAlgorithmType in the <membership> node to use SHA512 as its value instead of SHA1. A snippet is given below:

<membership defaultProvider="sitecore" hashAlgorithmType="SHA512">

For both of these files, simply save a copy on your disk, then add the updated files to the Sitecore 9 archive. Once this is done, you can use SIF as normal to install a fresh instance of Sitecore 9 that uses the stronger SHA512 hashing algorithm.

Conclusion

This post was a very long-winded way of saying that three lines across two files need to be changed in order to upgrade the hashing algorithm on fresh installs using the Sitecore Install Framework. However, this method will also work with whatever algorithm you would like to use, as long as the configurations are set correctly. In my next post, we’ll be taking a deep dive into SQL Server Management Studio and discovering what actually takes place at each step of this process, with explanations and screenshots galore.

A special thanks to Corey Smith for help with discovery on this topic.

Comment below if you have any tips to add!

Leave a Reply