EasyHostingASP.NET | Best and cheap ASP.NET hosting.  This article will take a look at how Drupal handles authentication and how it can be extended to handle new methods, such as those used by a legacy system. We will then go on to take a look at the hashing algorithm used on a .Net site and how we were able to implement some Drupal code to ensure that the hashes could be understood by Drupal. This took a fair bit of detective work and the main point of the article is to document how we did it.

asp-net-drupal-user-migrate

Authentication in Drupal

Drupal contains the logic for user authentication in /includes/password.inc. An important function is user_check_password() where the first three characters of the $stored_hash are used to define the $type of password. A Drupal 7 password is denoted as ‘$S$’ as can be seen from the code below.

/includes/password.inc

The secret to defining your own hashing algorithm is to replace this function with another, and this can be done with a small module which swaps out the password.inc file. By implementing your own “myauthmodule” you can swap out the password.inc file and include your own logic as desired.

/sites/all/modules/myauthmodule/myauthmodule.install

As you can see we use our own custom password.inc when the modules is enabled, and revert back to the old one when the module is disabled.

This is what our new user_check_password() looks like.

/sites/all/modules/myauthmodule/password.inc

In this case, if the hash starts with ‘$X$’ our custom algorithm will kick in and check the $password as entered against the $stored_hash. It’s up to you to define the correct algorithm so that the $password is transformed into something which can be compared against the $stored_hash. If there is a match, the user will be authenticated.

.Net hashing algorithm

We will now go on to examine the hashing algorithm used in the .Net web application. The most difficult piece of the puzzle was working out exactly what algorithm was being used for the hash. After a lot of poking around we discovered that the .Net application was using SHA1 with 1000 iterations (RFC 2898). We also have to pick apart the string we were given by base64 decoding the string and then pulling the salt off the front of it.

/sites/all/modules/myauthmodule/crypt.inc

As we were unable to rely on the hash_pbkdf2() function existing in PHP (supported from PHP 5 >= 5.5.0) we had to code our own as a fallback. The PHP code for the hash algorithm was taken from comment posted on PHP hash_pbkdf2 manual page

/sites/all/modules/myauthmodule/hash_pbkdf2_fallback.inc (this file is not needed if you are using PHP 5 >= 5.5.0)

Getting it all to work

Now that our custom code is in place we can give it a spin with some real live data. The first step of the process is to import the data into your users table. The string you write into the pass column must include the following concatenated items:

  • the hash type, in this case ‘$X$’,
  • the hash

In our case the hash string was base64 encoded and was concatenation of 3 parts:

  • null character (8 bits)
  • 16 characters of salt (128 bits)
  • 32 characters of subkey (256 bits)

The hash string, as extracted from the legacy database looked similar to the following:

AFnR63Ykym/kDXLFEM5tlL450Y+drbfdwRGhsOCOMlcR273QYod3QZdKwhiKHKHjXw==

After it was written to the password column in the users table it looked like:

$X$AFnR63Ykym/kDXLFEM5tlL450Y+drbfdwRGhsOCOMlcR273QYod3QZdKwhiKHKHjXw==

With that knowledge (we had to learn and investigate to acquire them in first place) we were able to extract the legacy .Net salt and use it for calculation of our new subkey. The subkey is generated by hash_pbkdf2() function mentioned before, but to get the right subkey we need to provide correct settings and inputs (the order is exactly the same as the hash_pbkdf2() function requires):

  • the hash algorithm to be used (in our case sha1)
  • a password provided by a user, the legacy .Net salt
  • the number of iterations for the key derivation process (in our case 1000)
  • the length of the derived key in bytes (in our case 32)
  • the raw output set to TRUE to get our new subkey in raw binary format

To see clearly how the mechanism of getting .Net legacy hash is working, here’s the code again:

The _myauthmodule_crypt() function returns a newly calculated hash based on the password provided and the salt we extracted from the stored hash. This is combined with the prefix and the whole result is returned back to the calling function where it is compared with the stored hash.

The hash construction by .Net:

To make the hash construction more clear, we can look at it from .Net perspective. The .Net web has its own specific salt. It’s represented in hexadecimal format, let it be: 59d1eb7624ca6fe40d72c510ce6d94be (32 chars). User has provided some password, let it be: UserPassword . After the necessary operations are done we have the hash: AFnR63Ykym/kDXLFEM5tlL450Y+drbfdwRGhsOCOMlcR273QYod3QZdKwhiKHKHjXw== . So now we need to understand what are the necessary operations that creates hash from salt and user password inputs. Below is PHP code with hash_example() function that provides step by step example:

/sites/all/modules/myauthmodule/doc/hash_example.inc

As we are unable to rely on the hex2bin() function existing in PHP (supported from PHP 5 >= 5.4.0) we had to code our own as a fallback. The PHP code for the hash algorithm was taken from comment posted on PHP hex2bin manual page

/sites/all/modules/myauthmodule/doc/hex2bin_fallback.inc

Conclusion

This kind of approach is used for all migrations from legacy systems where users need to be migrated. Generally it is a fairly simple approach as the hashing algorithm is either simple or well documented. In this case we had to do a fair deal of sleuthing to work out how to do it. We hope that this article will be of help to other developers who are neck deep in salts and hashes.

Alexia Pamelov