简体   繁体   中英

Validate a salted hash

I am new to C# and this is my first question here so I apologize in advance for any faux pas.

Context:

When a user registers I call the CreateSaltedHash() method and pass it the user inputted password from the text field. This method salts and hashes the password before storing it in the Password column of my User table.

Question:

How should I validate the password when a user tries to log in?

If I call the CreateSaltedHash() method again it will not match because of the random salt.

Should I be storing the salts in a separate column? Should I be using a delimiter when generating the salted hash? What is the most secure way of validating the input password against the salted and hashed password?

Code: This is what I have so far.

public class PasswordHash
{
    public const int SALT_BYTES = 32;

    /*
     * Method to create a salted hash
     */
    public static byte[] CreateSaltedHash(string password)
    {
        RNGCryptoServiceProvider randromNumberGenerator = new RNGCryptoServiceProvider();
        byte[] salt = new byte[SALT_BYTES];
        randromNumberGenerator.GetBytes(salt);
        HashAlgorithm hashAlgorithm = new SHA256Managed();
        byte[] passwordByteArray = Encoding.UTF8.GetBytes(password);
        byte[] passwordAndSalt = new byte[passwordByteArray.Length + SALT_BYTES];
        for (int i = 0; i < passwordByteArray.Length; i++)
        {
            passwordAndSalt[i] = passwordByteArray[i];
        }
        for (int i = 0; i < salt.Length; i++)
        {
            passwordAndSalt[passwordByteArray.Length + i] = salt[i];
        }
        return hashAlgorithm.ComputeHash(passwordAndSalt);
    }

    public static bool OkPassword(string password)
    {
        //This is where I want to validate the password before logging in.
    }
}


Calling the method in the Register class.

User user= new User();
user.password = PasswordHash.CreateSaltedHash(TextBoxUserPassword.Text);

You could use Bcrypt.Net ; it has a lot of recommendations for being really secure, plus it is very easy to use. As I understand it, when you create the password it automatically generates a unique salt for you, which is then stored in the hashed password string; so you do not store the salt separately, but in the same field as the hashed password. The point is each password has it own salt, which makes it much more difficult (time consuming) for a hacker to crack multiple passwords. The algorithm Bcrypt uses is also CPU intensive, so it requires a lot of computational power (=money) to crack.

Jeff Atwood (stackoverflow moderator) recommends Bcrypt .

When you first generate the hash, you need to store both the salt and the final hash - then re-use that same salt for future comparisons.

So you'd change your CreateSaltedHash method to take a password and a salt, and write a new CreateSalt method to generate the salt when a password is created/changed which is stored alongside the final hash.

I Suggest you to use SaltedHash of ServiceStack which you can install it from your Nuget. Simply Enter Install-Package ServiceStack in your Nuget Console, then you'll be able to use the following imports in your code.

using ServiceStack.ServiceInterface.Auth;

And then you would generate your salt and hash so much easier and absolutely faster than before. Just enter the following codes:

class Security
{
   ...
   public void generate(string Password)
   {
   string hash, salt;
   new SaltedHash().GetHashAndSaltString(Password,out hash,out salt);
   //Store the hash and salt
   }
   ...
}

And yes , You must store the hash and salt to be able to run your OkPassword Method.

public bool OkPassword(string Password)
{
   var hash = //getStoredHash
   var salt = //getStoredSalt
   bool verify = new SaltedHash().VerifyHashString(Password, hash , salt);
   return verify ;
}

You will have to store the salt along with the hash.

Refer to this article to get some background information: http://www.h-online.com/security/features/Storing-passwords-in-uncrackable-form-1255576.html

As the other answers states; yes you should store the salt or derive it from for example the username.

You should also use Rfc2898DeriveBytes to make it more secure.

Here is a good article on that topic: Password salt and hashing in C#

Should I be storing the salts in a separate column?

Yes.

Should I be using a delimiter when generating the salted hash?

Not necessary, but it also won't hurt as long as you include the same delimiter when validating.

What is the most secure way of validating the input password against the salted and hashed password?

SHA512, SHA-2 or -3 would be more secure than SHA256, but do you need that much more security?

You can either store the salt, or use the same salt every time. I recommend storing the salt as it is more secure than using the same salt across all users.

Most of my tables have a column with the date time of when the row was created. I use the Ticks property of the DateTime struct of this value to salt the hash, but you can use anything you like, as long as you use the same salt for the user each time. One thing to watch out for is if you use this method, and you are using the SQL type DateTime (and not DateTime2), then there is a precision issue. If you create the DateTime in code, you will need to truncate it (I believe to hundredths of a second).

And for the longer answer -

The has should be random with every password created. This will make it unique in itself. Due to this 'randomness', you will almost never be able to programatically find the hash associated to the file.

The way you encrypted the password (without the hash) should be the same, so using a reverse of this method will be sufficient every time.
PS: (A more secure way o validating) You can either reverse the encrypted password to its original [Hard] , or encrypt the validating password with the hash, and make sure that encrypted password matches that stored in the DB [Preferred] .

So, you will need to store the encrypted password, as well as the hash associated to it, in the database.

This will be the way to gather all the information needed to validate the password.

Because you are generating a random salt you would need to store the salt on the database. The problem with that is that if your database becomes compromised, the attacker will have the salt and the hashed password, so they can more easily determine the real password. Ideally you should have a static salt in your code so that if your database is compromised they still don't have the salt and if your code is compromised, they don't have the database just yet.

Another solution could be to use pepper. Pepper is similar to salt but you don't store it the database with the salt and hashed password. It would be stored in the code. This way you have a random salt that is generated and a constant which is stored separate. To make the pepper more random, you could create a sub-string of a larger string that you are using for a pepper which is offset based on some variable, such as the user id. This again is an internal thing that an attack would not know of if they managed to get your data.

You should save the digest and salt. The iterations and digestLength values can be constants in your application.

byte[] getNewSalt(Int32 size)
{
    RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
    byte[] salt = new byte[size];
    rng.GetBytes(salt);
    return salt;
}


byte[] getPasswordDigest(byte[] value, byte[] salt, Int32 iterations, Int32 digestLength)
{
    Rfc2898DeriveBytes deriveBytes = new Rfc2898DeriveBytes(value, salt, iterations);
    return deriveBytes.GetBytes(digestLength);
}

Recent articles suggest that to further secure passwords, you can split up the password into parts, hash the individual parts, then store them in separate tables in the DB.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM