System.Web.Helpers.Crypto - Where's the salt?

2020-05-18 09:12发布

问题:

In the past when dealing with passwords I've always stored a salt and a hashed password separately in my data store. Today I was looking to update some legacy code to use a RFC 2898 hash value. I came across the Crypto.Hash methods from System.Web.Helpers. It looks like these will do most of the heavy lifting for me. There are GenerateSalt(), HashPassword(), and VerifyHashedPassword() methods. The HashPassword() and VerifyHashedPassword() methods don't take a salt value. The MSDN documentation for HashPassword() method says:

"The format of the generated hash bytestream is {0x00, salt, subkey}, which is base-64 encoded before it is returned."

Do I need to worry about a salt? The documentation seems to say that a salt will be generated automatically and stored in the base-64 encoded value? Is this correct? All I need to store is the string returned from HashPassword()?

回答1:

Answer

All passwords need to be salted in order to hash them securely. In this case, however, you are correct. System.Web.Helpers.Crypto takes care of creating a salt for you. You don't need to create one. It is stored in the string returned by Crypto.HashPassword().

Example

All you need to do is something like this.

using System.Web.Helpers;

public void SavePassword(string unhashedPassword)
{
    string hashedPassword = Crypto.HashPassword(unhashedPassword);
    //Save hashedPassword somewhere that you can retrieve it again.
    //Don't save unhashedPassword! Just let it go.
}

public bool CheckPassword(string unhashedPassword)
{
    string savedHashedPassword = //get hashedPassword from where you saved it

    return Crypto.VerifyHashedPassword(savedHashedPassword, unhashedPassword)
}

More Information

  • If you would like to see the source code for the Crypto class you can view it here.
  • And here is a good blog on the class and some of the ideas behind it.


回答2:

Just wanted to share this post:

http://forums.asp.net/t/1842429.aspx?System+Web+Helpers+Crypto+HashPassword

Hello,
I have been looking at the new Crypto HashPassword and VerifyMethod method in System.Web.Helpers.
Both methods use a Salt but they do not return the Salt only the Hash.
Don't I need the Salt to store it in the database? Am I missing something?
Thank you,
Miguel

..

You'd generate the salt when the user creates the account, and then you'd append it to the password before you call HashPassword. You'd then store that salt and the hashed password in your DB. When you validate credentials just read the salt from the DB and appends it to the password from the login before calling VerifyHashedPassword. BrockAllen

..

Not sure if I understood it ... Because in HashPassword a Salt is generated inside the method:

Miguel

// Method in Crypto class
public static string HashPassword(string password)
{
    if (password == null)
    {
        throw new ArgumentNullException("password");
    }

    // Produce a version 0 (see comment above) password hash.
    byte[] salt;
    byte[] subkey;
    using (var deriveBytes = new Rfc2898DeriveBytes(password, SaltSize, PBKDF2IterCount))
    {
        salt = deriveBytes.Salt;
        subkey = deriveBytes.GetBytes(PBKDF2SubkeyLength);
    }

    byte[] outputBytes = new byte[1 + SaltSize + PBKDF2SubkeyLength];
    Buffer.BlockCopy(salt, 0, outputBytes, 1, SaltSize);
    Buffer.BlockCopy(subkey, 0, outputBytes, 1 + SaltSize, PBKDF2SubkeyLength);
    return Convert.ToBase64String(outputBytes);
}

..

BrockAllen Sep 11, 2012 07:50 PM

Yea, ignore that -- it's an internal salt for the Rfc2898DeriveBytes and it's derived from the password so it's not a true salt in the context of storing passwords since it's consistently re-created for the same password. You will want to still do your own salt at the password string level. So something like this:

public void CreateAccount(string username, string password)
{
    var salt = Crypto.GenerateSalt();
    var saltedPassword = password + salt;
    var hashedPassword = Crypto.HashPassword(saltedPassword);
    CreateAccount(username, salt, hashedPassword);
}

public void Verify(string username, string password)
{
    var salt = GetSaltForUserFromDatabase(username);
    var hashedPassword = GetHashedPasswordForUserFromDatabase(username);
    var saltedPassword = password + salt;
    if (Crypto.VerifyHashedPassword(hashedPassword, saltedPassword))
    {
        // valid password for this username
    }
}

This will allow you to store your salt in the database along with the hashed salted password.

Additional info on password hashing with salt in general from this post: https://crackstation.net/hashing-security.htm

To Store a Password

  1. Generate a long random salt using a CSPRNG.
  2. Prepend the salt to the password and hash it with a standard cryptographic hash function such as SHA256.
  3. Save both the salt and the hash in the user's database record.

To Validate a Password

  1. Retrieve the user's salt and hash from the database.
  2. Prepend the salt to the given password and hash it using the same hash function.
  3. Compare the hash of the given password with the hash from the database. If they match, the password is correct. Otherwise, the password is incorrect.

Source Code for Crypto class:

http://aspnetwebstack.codeplex.com/SourceControl/latest#src/System.Web.Helpers/Crypto.cs