32

过去在处理密码时,我总是在我的数据存储中分别存储盐和散列密码。今天我正在寻找更新一些遗留代码以使用 RFC 2898 哈希值。我Crypto.HashSystem.Web.Helpers. 看起来这些将为我完成大部分繁重的工作。有GenerateSalt()HashPassword()VerifyHashedPassword()方法。HashPassword()VerifyHashedPassword()方法不采用盐值。方法的 MSDN 文档HashPassword()说:

“生成的哈希字节流的格式是{0x00, salt, subkey},在返回之前经过base-64编码。”

我需要担心盐吗?文档似乎说会自动生成盐并存储在base-64编码值中?这个对吗?我需要存储的只是从返回的字符串HashPassword()

4

2 回答 2

56

回答

所有密码都需要加盐才能安全地散列它们。但是,在这种情况下,您是正确的。System.Web.Helpers.Crypto 负责为您创建盐。不需要创建一个。它存储在 Crypto.HashPassword() 返回的字符串中。

例子

您需要做的就是这样的事情。

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)
}

更多信息

  • 如果您想查看 Crypto 类的源代码,可以在此处查看
  • 这是一个关于课程好博客以及它背后的一些想法。
于 2013-07-20T03:01:21.270 回答
-2

只想分享这篇文章:

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

您好,
我一直在研究 System.Web.Helpers 中的新 Crypto HashPassword 和 VerifyMethod 方法。
这两种方法都使用 Salt,但它们不会只返回 Salt 哈希。
我不需要 Salt 将其存储在数据库中吗?我错过了什么吗?
谢谢你,
米格尔

..

您将在用户创建帐户时生成盐,然后在调用 HashPassword 之前将其附加到密码。然后,您将该盐和散列密码存储在您的数据库中。当您验证凭据时,只需从数据库中读取盐并将其附加到登录密码中,然后再调用 VerifyHashedPassword。布罗克艾伦

..

不知道我是否理解......因为在 HashPassword 方法内部生成了一个 Salt:

米格尔

// 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);
}

..

布罗克艾伦 2012 年 9 月 11 日晚上 7:50

是的,忽略它——它是 Rfc2898DeriveBytes 的内部盐,它是从密码派生的,因此在存储密码的上下文中它不是真正的盐,因为它始终为相同的密码重新创建。您仍然希望在密码字符串级别做自己的盐。所以是这样的:

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
    }
}

这将允许您将盐与散列加盐密码一起存储在数据库中。

这篇文章中有关使用盐进行密码散列的其他信息: https ://crackstation.net/hashing-security.htm

存储密码

  1. 使用 CSPRNG 生成长随机盐。
  2. 将盐添加到密码中,并使用标准加密散列函数(例如 SHA256)对其进行散列。
  3. 将盐和哈希都保存在用户的数据库记录中。

验证密码

  1. 从数据库中检索用户的盐和哈希。
  2. 将盐添加到给定密码并使用相同的散列函数对其进行散列。
  3. 将给定密码的哈希值与数据库中的哈希值进行比较。如果它们匹配,则密码正确。否则,密码不正确。

Crypto类的源代码:

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

于 2014-07-01T07:14:57.710 回答