0

I saw a video detailing how to write a simple salted hash program in C# here. Below is the code they wrote (slightly edited for console applications):

using System;
using System.Text;
using System.Security.Cryptography;

namespace MyApplication
{
    class Program
    {
        const int SALT_SIZE = 10;

        static void Main(string[] args)
        {                                
            string salt = CreateSalt();
            string password = "securePassword";
            string hashedPassword = GenerateSHA256Hash(password, salt);

            Console.WriteLine("salt: " + salt);
            Console.WriteLine("hashedPassword: " + hashedPassword);                                   
        }

        private static string CreateSalt()
        {
            var rng = new RNGCryptoServiceProvider();
            var buffer = new byte[SALT_SIZE];
            rng.GetBytes(buffer);

            return Convert.ToBase64String(buffer);
        }

        private static string GenerateSHA256Hash(string input, string salt)
        {
            byte[] bytes = Encoding.UTF8.GetBytes(input + salt);
            var hashManager = new SHA256Managed();
            byte[] hash = hashManager.ComputeHash(bytes);

            return ByteArrayToHexString(hash);
        }

        private static string ByteArrayToHexString(byte[] bytes)
        {
            StringBuilder sb = new StringBuilder(bytes.Length * 2);

            foreach (byte b in bytes)
                sb.AppendFormat("{0:x2}", b);

            return sb.ToString();
        }
    }
}

From what I've read online, salted hashes are one of the most secure ways of storing passwords. However, I have a few questions:

  1. I have read that it's not enough to hash a salted password once. You need to keep hashing it thousands of times to make brute-forcing more difficult for attackers.

    Would doing something like below be more secure, and what would be a good number of times to repeat hashing?

    var hash = hashManager.ComputeHash(bytes);
    
    for (int i = 0; i < 10000; i++)
        hash = hashManager.ComputeHash(hash);
    

    I've also read that you also need to include the salt when rehashing, but I don't understand how to add it properly.

  2. For the salt buffer size, is 10 a good number to use, or would a higher/lower number be more secure (e.g. 16)?

  3. I'm taking this with a grain of salt, but I've read that SHA256 isn't a secure choice anymore because it's too fast, meaning brute forces are quicker to do.

    Does this mean that fast algorithms like SHA are obsolete and need to be replaced with slower algorithms like bcrypt?

  4. I assume that Hex Strings are a secure way to store salted hashes. Is this correct?

  5. After applying all changes from the above questions (if any), would the above code be secure enough to use in a production setting?

  • 1
    "would the above code be secure enough to use in a production setting?" -- absolutely not. Is there a reason why you are not using the many established, tested, and verified password hashing libraries? Have you looked at things like bcrypt's approach to answer some of your questions? – schroeder Mar 30 '20 at 08:16
  • 2
    Writing hash algorithms ***for learning purposes*** is fine and a good way to learn, but as soon as you suggest that you want to use your own hash code in production, then the conversation changes, Reas this: https://security.stackexchange.com/questions/18197/why-shouldnt-we-roll-our-own/18198#18198 – schroeder Mar 30 '20 at 08:18
  • @schroeder Not at all! I have no plans to write my own hash code. I thought using something like SHA256 was using hash code standards. Did I miss something? – Floating Sunfish Mar 30 '20 at 09:50
  • @schroeder Also, I haven't looked at `bcrypt` just yet but it's next on my to-do list. Many thanks for commenting! – Floating Sunfish Mar 30 '20 at 09:58

1 Answers1

3

I have read that it's not enough to hash a salted password once. You need to keep hashing it thousands of times to make brute forcing more difficult for attackers.

That is right. You want your password hash function to be slow, so that an offline brute-force attack would also be slow.

hash = hashManager.ComputeHash(hash);

You really want to use the password within the iteration, to avoid the possibility of precomputation. If your algorithm does h=sha1(h) 10,000 times, I can create a table (or perhaps even an algorithm?) that applies sha1 10,000 times. I have to do this work only once for all passwords, and I can do it before your database is even leaked. So typically, the password is included in each iteration.

The HMAC primitive is typically used to hash multiple things together, but for learning purposes, simply concatenating values together is also acceptable.

For the salt buffer size, is 10 a good number to use, or would a higher/lower number be more secure (e.g. 16)?

Preferably, each password has a unique salt. So your salt should be long enough to accomplish this. 10 bytes is sufficient, and making this longer doesn't really give better security.

I'm taking this with a grain of salt, but I've read that SHA256 isn't a secure choice anymore because it's too fast, meaning brute forces are quicker to do.

SHA256 by itself is fast. That is why you iterate it many times, which makes it slow again.

Does this mean that fast algorithyms like SHA are obsolete and need to be replaced with slower algorithms like bcrypt?

For password hashing, yes. For password hashing you should use PBKDF2, Argon2, bcrypt, or scrypt.

However, there are many other applications for hashes, and SHA256 is fine for many applications, so I wouldn't say that SHA256 is obsolete.

I assume that Hex Strings are a secure way to store salted hashes. Is this correct?

Yes.

After applying all changes from the above questions (if any), would the above code be secure enough to use in a production setting?

Since you don't seem to know what you are doing, and this is critical security code, I would not use it in production. Also, there would be no reason not to use the built-in Rfc2898DeriveBytes class, which does PBKDF2.

Also, I wrote a blog post about Requirements for iterative password hashing.

Sjoerd
  • 28,707
  • 12
  • 74
  • 102
  • Many thanks for the answer! I am indeed just studying password hashing concepts for eventual use in production because it's such a standard practice nowadays. I'll wait for other inputs for now, but this one was very informative! – Floating Sunfish Mar 30 '20 at 09:54
  • 3
    @FloatingSunfish If you wish to use code in production, please use a ready-made library for the purpose of password hashing. In the best possible case, there is a `Password.Hash(string password)` and a `Password.Verify(string password, Hash hash)` function. –  Mar 30 '20 at 10:05
  • @MechMK1 I see... Will do! ;) – Floating Sunfish Mar 30 '20 at 10:38