Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C#

Hashing Passwords in .NET Core with Tips

4.43/5 (9 votes)
3 Jun 2016CPOL11 min read 35.4K  
In this post, I talk about hashing the passwords in .NET Core.

Previously, I had written some stuff for .NET Framework and how to implement basic security concepts on your applications that are working in .NET environment. In this post, I want to walk you through how to implement the same security concepts in your applications that are based on the .NET Core Framework. As always, there will be 2 topics that I will be covering in this post of mine. I did so before but since that was for .NET itself, I don’t think that works with .NET Core. Besides, .NET Core is different in this matter as compared to .NET framework, one of the major reasons being that there is no “SHA256Managed” (or any other _Managed types in the framework). So the framework is different in this manner. This post would cover the basic concepts and would help you to understand and get started using the methodologies for security.

Security_original

Figure 1: Data security in your applications is the first step for gaining confidence in clients

First of all, I would be covering the parts of hashing and I will give you a few of my tips and considerations for hashing the passwords using .NET Core in your applications. Before I start writing this post, I remember when I was working in Mono Project and the platform was very easy to write for. I was using Xamarin Studio as IDE and the Mono was the runtime being used at that time, in my previous guide although the focus was on the Mono programming on Ubuntu, in this post, I will covering the concepts of the same but with .NET Core. .NET Core is really beautiful, although it is not complete, yet it is very powerful. I am using the following tools at the moment so in case you want to set up your own programming environment to match mine, you can use them.

  1. IDE: Visual Studio Code
  2. C# extension: For C# support and debugging
  3. Terminal: Ubuntu provides a native terminal that I am using to execute the command to run the project after I am done working with my source code.

Screenshot (967)

Figure 2: Visual Studio being used for C# programming using .NET Core

You can download and install these packages on your own system. If you are using Windows, I am unaware as to what Visual Studio Code has to offer, because since the start of Visual Studio Code I have just used it on Ubuntu and on Windows systems, my preference is always Visual Studio itself. Also, I am going to use the same project that I had created and I am going to start from there, A Quick Startup Using .NET Core On Linux.

So, let’s get started…

Hashing Passwords

Even before starting to write it, I am considering the thunderstorm of comments that would hit me if I make a small and simple mistake in the points here, such as:

  1. Bad practices of hashing
  2. Not using the salts
  3. Bad functions to be used
  4. Etc.

However, I will break the process down since it is just a small program that does the job and there is no less exaggeration here. Instead of talking about that, I will walk you through many concepts of hashing and how hackers may try to get the passwords where hashing helps you out.

Until now, I have written 3 to 4 articles about hashing, and I can’t find any difference in any of these codes that I have been writing. The common difference is that there is no extra managed code stuff around. .NET Core removed everything redundant in the code samples. So we are left with the simple ones now that we would be using.

What I did was that I just created a simple minimal block of the SHA256 algorithm that would hash the string text that I am going to pass. I used the following code:

C#
// SHA256 is disposable by inheritance.
using (var sha256 = SHA256.Create()) {
    // Send a sample text to hash.
    var hashedBytes = sha256.ComputeHash(Encoding.UTF8.GetBytes("hello world"));
 
    // Get the hashed string.
    var hash = BitConverter.ToString(hashedBytes).Replace("-", "").ToLower();
 
    // Print the string. 
    Console.WriteLine(hash);
}

This code is a bit different from the one being used in .NET Framework. In the case of .NET Framework, the code starts as:

C#
using (var sha256 = new SHA256Managed()) {
     // Crypto code here...
}

That is the only difference here, rest of the stuff is almost alike. The conversion of bytes into string text is upto you. You can either convert the bytes to hexadecimal strings or you can use the BitConverter helper to convert that to the text that is being represented.

The result of this code is:

Screenshot (968)

Figure 3: Result of the above shown code in C# being executed in Ubuntu terminal on .NET Core runtime

There is one another constraint here, “Encoding.UTF8“, if you use another encoding for characters, then the chances are your hashed string would be different. You can try out other flavors of the character encodings such as:

  1. ASCII
  2. UTF-8
  3. Unicode (.NET Framework takes Unicode encoding as UTF-16 LE)
  4. Rest of the encodings of Unicode, etc.

The reason is that they provide a different byte ordering and this hashing function works on the bytes of the data that are passed.

Tips and Considerations

There are generally two namespaces rising, one of them is the very old familiar .NET’s namespace, System.Security.Cryptography, whereas another one is Microsoft.AspNet.Cryptography which is a part of ASP.NET Core and they are to be released. Anyways, here are a few of the tips that you should consider before handling the passwords.

Passwords Are Fragile — Handle With Care

I can’t think of any online service, offline privacy application, API hosts where passwords are not handled with care. If there is, I would still act as I never knew of it. Passwords must always be hashed before saving in the database. Hashing is done because hashing algorithms are created with one thing in mind, that they are hard (if not impossible) to convert back to plain-text passwords. This makes it harder for the hackers to get the passwords back in the real form. To explain this fact, I converted the code into a functional one and printed the hash with a little change in the text.

C#
private static string getHash(string text) {
    // SHA512 is disposable by inheritance.
    using (var sha256 = SHA256.Create()) {
        // Send a sample text to hash.
        var hashedBytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(text));
   
        // Get the hashed string.
        return BitConverter.ToString(hashedBytes).Replace("-", "").ToLower();
    }
}

I will execute this function and get the hashed string back for the text that has very less difference in them.

C#
string[] passwords = { "PASSWORD", "P@SSW0RD", "password", "p@ssw0rd" };
 
foreach (var password in passwords) {
    Console.WriteLine($"'{password}': '{getHash(password)}'");
}

Although they seem to look alike but have a look at the avalanche effect that happens due to such small changes. Even have a look at the differences in the capital case and small case.

Screenshot (972)

Figure 4: Password hashes being shown in the terminal

This helps in many ways, because it is harder to guess what the possible plain-text alternate would be for this hashed string. Remember the constraints again:

  1. The character encoding is UTF-8; others would provide a different character encoding bytes ordering.
  2. Hash algorithm being used in SHA256, others would produce even different results.

If you don’t hash out the passwords, hackers may try to use most common attacks on your database system to gain privileges of access. A few common type of attacks are:

  1. Brute force attack
  2. Dictionary attack
  3. Rainbow table attack

Rainbow table attack works in a different manner, it tries to convert the hash back to the plain-text based on the database where a password/hash combination is present. Brute force and dictionary attacks use a guessing and commonly used passwords respectively, to gain access. You need to prevent these attacks from happening.

Besides, there are cases where your password hashing is useless. Such as when you want to use MD5 hashing algorithms. MD5 algorithms can be easily cracked and the tables for entire password look up are already available and hackers can use those tables to crack your passwords that are hashed using MD5. Even SHA256, SHA512 don’t work as you are going to see in the following section. In such cases, you have to add an extra layer of security.

Bonus: How to Break It?

Before I continue further, I wanted to share the point of these passwords and their hashes being weaker. There are many hacking tools available, such as reverse look ups. Let us take our first password and see if that can be cracked. I used CrackStation service to crack the password and convert it back to its original text form,

Screenshot (975)

Figure 5: SHA256 based password converted back to its original form

See how inefficient even these tricks happen to be. In a later section, I will show you how to salt the passwords and what the effect is. Although we had hashed it using SHA256, the reverse lookup table already has that password of ours. Hackers would just try to use that hash and get the real string value in the plain-text to be used for authentication purposes.

Slower Algorithms

On networks where hackers are generally going to attack your websites with a script, you should have a hashing algorithm that is (not very) significantly slow. About half a second or third of a second should be enough. The purpose is:

  1. It should add a delay to the attacker if they are trying to run a combination of passwords to gain access.
  2. It should not affect the UX.

There are many algorithms that keep the iterations to a number of 10,000 or so. The namespace that I had talked of, Microsoft.AspNet.Cryptography, has the objects that allow you to specify the iteration, salt addition, etc.

Remember: For online applications, do not increase the iteration count. You would indirectly cause a bad UX for the users who are waiting for a response.

Add Salt to the Recipe

I wonder who started the terminology of salt in cryptography. He must have a good taste in computers, I’d say. I did cover most of the parts of adding the salts in the article that I have added in the references section, please refer to that article. However, I would like to share the code that I have used to generate a random salt for the password. Adding the salt would help you randomize the password itself. Suppose, a user had a password of, “helloserver”, another one had the same password too. By default, the hash would be alike but if you add a random salt to it, it would randomize the password.

In .NET Core, you can use the “RandomNumberGenerator” to create the salt that can be used for the password.

C#
private static string getSalt() {
    byte[] bytes = new byte[128 / 8];
    using (var keyGenerator = RandomNumberGenerator.Create()) {
        keyGenerator.GetBytes(bytes);
 
        return BitConverter.ToString(bytes).Replace("-", "").ToLower();
    }
}

This would create a few random bytes and then would return them to be used for the passwords.

C#
string[] passwords = { "PASSWORD", "P@SSW0RD", "password", "p@ssw0rd" };
 
foreach (var password in passwords) {
    string salt = getSalt();
    Console.WriteLine($@"{{
       'password': '{password}', 
       'salt': '{salt}',
       'hash': '{getHash(password + salt)}'
       }}"
    );
}

This shows how the passwords with “random salt” differ.

Screenshot (974)

Figure 6: Passwords with their salts being hashed

Have a look at the hashes now. The hashes differ from what they were before. Also, notice that the function returns a different salt every time which makes it possible to generate different hashes for even the similar passwords. One of the benefits of this is that your passwords would be secure from a rainbow table attack.

Test: We saw that unsalted passwords are easy to be reverse looked up. In this, case, we salted the password and we are going to test the last of our password to see if there is a match.

Screenshot (976)

Figure 7: Password not found

Great, isn’t it? The password was not matched against any case in the password dictionary. This gives us an extra layer of security because a hacker won’t be able to convert the password back to its original form by using a reverse look up table.

Using Salt: The Good Way

There is no good way of using the salt, there is no standard to be followed while adding the salt to the password. It is just an extra “randomstring to be added to your password strings before they are hashed. There are many common ways, some add salt to the end, some prepend it, some do both.

Do as you please. There are, however, a few tips that you should keep in mind while salting the passwords.

  1. Do not reuse the salts.
  2. Do not try to extract the salts from the passwords or usernames.
  3. Use suitable salt size; 128-bit?
  4. Use random salt.
    • You should consider using a good library for generating the salts.
  5. Store the salts and passwords together. Random salts won’t be created again (in the near future).

References

  1. Avalanche effect
  2. Hashing Passwords using ASP.NET’s Crypto Class
  3. Guide for building C# apps on Ubuntu: Cryptographic helpers
  4. What are the differences between dictionary attack and brute force attack?

Final Words

In this post, I demonstrated the hashing techniques in .NET Core, although the procedure is similar and very much alike. There are a few differences that the objects are not similar. The object instantiation is not similar and in my own opinion, this is also going to change sooner.

I gave you a good overview of password hashing, how to crack them (actually, how an attacker may crack them) and how you can add an extra layer of security. Besides, you should consider adding more security protocols to your own application to secure it from other hacking techniques too.

I was not able to finish the HMAC concepts in this post which I am going to work around in the second part of this article series along with sooner encryption.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)