【问题标题】:ASP.NET Identity - where is the salt stored?ASP.NET Identity - 盐存储在哪里?
【发布时间】:2017-05-13 21:00:06
【问题描述】:

我创建了一个简单的 MVC4 应用并注册了一个用户。用户名和密码存储在名为:AspNetUsers 的表中。此表没有盐字段。

我理解的方式是当用户登录时;他们输入用户名和密码。然后将盐与输入的密码连接起来,并与数据库中的密码进行比较。这不正确吗?即

Hash(PasswordEntered) + Salt = Password in database = authenticated
Hash(PasswordEntered) + Salt <> Password in database = not authenticated

有一个字段叫做:aspnetusers.SecurityStamp,但是我的研究告诉我这不是 Salt。

更新

我刚刚读过斯科特·张伯伦。请看以下步骤:

1)用户在注册时输入:Hello123作为密码,Salt(随机生成)为:456,则输入PasswordHash的密码为:Hello123+456
2) 然后用户尝试登录并输入 Hello123(正确)作为密码。 salt(随机生成)为:567。因此将Hello123+456与Hello123+567进行比较,认证失败。

在这种情况下,用户输入了正确的密码并且未通过身份验证。我显然在这里遗漏了一些基本的东西。

【问题讨论】:

  • 在我的 AspNetUsers 数据库中,有一个名为 [PasswordHash] 的苍蝇
  • @federico scamuzzi,谢谢。盐不是分开存放的吗?
  • 不..我不认为
  • 根据您的更新,您缺少一个步骤。写入数据库的值为456+Hash(Hello123+456),当您去测试密码时,它会执行类似于Hash(UserEnteredPassword+SavedPassword.Substring(0,3)) == SavedPassword.Substring(3, SavedPassword.Length-3)的操作
  • @Scott Chamberlain,在您上面的评论中,我相信应该说:456+Hash(456+Hello123) 而不是:456+Hash(Hello123+456)。对吗?

标签: c# asp.net


【解决方案1】:

你的模式不正确,正确的应该是

Hash(PasswordEntered + Salt)  = hash in database = authenticated
Hash(PasswordEntered + Salt)  <> hash in database = not authenticated

ASP.net 提供程序的工作方式是将Salt + Hash(PasswordEntered + Salt) 存储在密码字段中。因此,当您去测试密码时,您只需使用盐中分隔符之前的部分并将其与分隔符之后的部分进行比较。

在您的更新中,您有错误的部分是用户登录时没有随机生成的盐。它重新使用在用户注册时随机生成的盐,该盐以纯文本形式存储在数据库并且没有散列。

【讨论】:

  • 谢谢。在这种情况下,每个用户的 Salt 值必须相同(因为无法在每个用户的基础上识别它)。那是对的吗? +1 用于更正公式。
  • 不,为了使盐有用,每个散列都应该有一个唯一的盐。用于盐的值不是秘密信息,不需要隐藏。有关其存储方式的更多详细信息,请参阅this answer
  • 不,salt 存储在加盐和散列密码旁边的同一列中
  • @Scott Chamberlain,我已根据您的回答更新了问题。你能看看吗?我快到了。
  • 通常只是存储在另一个数据库字段中或只是附加在哈希前面。盐不是秘密。例如,我有一个 1234 的盐,在我的数据库中我会存储 1234|avsSAFwa41s== 所以1234 是盐,avsSAFwa41s== 是 base64 编码的密码。当我去验证密码时,我会做 Hash(1234 + user provided password) 并检查它是否匹配 avsSAFwa41s==
【解决方案2】:

对于 asp.net core 3 也是如此。

Salt + Hash 被连接并存储到数据库中的passwordHash 字段中。

这里是如何生成密码哈希的源代码。

*password参数为明文密码。

*subkeyhash

https://github.com/dotnet/aspnetcore/blob/master/src/Identity/Extensions.Core/src/PasswordHasher.cs

private static byte[] HashPasswordV3(string password, RandomNumberGenerator rng, KeyDerivationPrf prf, int iterCount, int saltSize, int numBytesRequested)
{
    // Produce a version 3 (see comment above) text hash.
    byte[] salt = new byte[saltSize];
    rng.GetBytes(salt);
    byte[] subkey = KeyDerivation.Pbkdf2(password, salt, prf, iterCount, numBytesRequested);

    var outputBytes = new byte[13 + salt.Length + subkey.Length];
    outputBytes[0] = 0x01; // format marker
    WriteNetworkByteOrder(outputBytes, 1, (uint)prf);
    WriteNetworkByteOrder(outputBytes, 5, (uint)iterCount);
    WriteNetworkByteOrder(outputBytes, 9, (uint)saltSize);
    Buffer.BlockCopy(salt, 0, outputBytes, 13, salt.Length);
    Buffer.BlockCopy(subkey, 0, outputBytes, 13 + saltSize, subkey.Length);
    return outputBytes;
}

【讨论】:

    猜你喜欢
    • 2015-11-08
    • 1970-01-01
    • 2013-07-07
    • 1970-01-01
    • 2010-11-16
    • 2011-05-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多