ASP.NET Identity的默认密码哈希 - 它是如何工作的并且是否安全?

时间:2013-12-16 22:08:43

标签: c# asp.net security passwords asp.net-identity

我想知道在MVC 5和ASP.NET Identity Framework附带的UserManager中默认实现的Password Hasher是否足够安全?如果是这样,如果你可以向我解释它是如何工作的?

IPasswordHasher界面如下所示:

public interface IPasswordHasher
{
    string HashPassword(string password);
    PasswordVerificationResult VerifyHashedPassword(string hashedPassword, 
                                                       string providedPassword);
}

正如你所看到的,它不需要盐,但在这个帖子中提到:“Asp.net Identity password hashing”  它确实在幕后制作它。所以我想知道它是如何做到的?这盐来自何处?

我担心的是盐是静态的,使其非常不安全。

4 个答案:

答案 0 :(得分:202)

以下是default implementation的工作原理。它使用带有随机盐的Key Derivation Function来生成哈希值。盐被列为KDF产出的一部分。因此,每次“哈希”相同的密码时,您将获得不同的哈希值。为了验证散列,输出被拆分回salt和其余部分,并且KDF再次运行在具有指定salt的密码上。如果结果与初始输出的其余部分匹配,则验证散列。

散列:

public static string HashPassword(string password)
{
    byte[] salt;
    byte[] buffer2;
    if (password == null)
    {
        throw new ArgumentNullException("password");
    }
    using (Rfc2898DeriveBytes bytes = new Rfc2898DeriveBytes(password, 0x10, 0x3e8))
    {
        salt = bytes.Salt;
        buffer2 = bytes.GetBytes(0x20);
    }
    byte[] dst = new byte[0x31];
    Buffer.BlockCopy(salt, 0, dst, 1, 0x10);
    Buffer.BlockCopy(buffer2, 0, dst, 0x11, 0x20);
    return Convert.ToBase64String(dst);
}

验证

public static bool VerifyHashedPassword(string hashedPassword, string password)
{
    byte[] buffer4;
    if (hashedPassword == null)
    {
        return false;
    }
    if (password == null)
    {
        throw new ArgumentNullException("password");
    }
    byte[] src = Convert.FromBase64String(hashedPassword);
    if ((src.Length != 0x31) || (src[0] != 0))
    {
        return false;
    }
    byte[] dst = new byte[0x10];
    Buffer.BlockCopy(src, 1, dst, 0, 0x10);
    byte[] buffer3 = new byte[0x20];
    Buffer.BlockCopy(src, 0x11, buffer3, 0, 0x20);
    using (Rfc2898DeriveBytes bytes = new Rfc2898DeriveBytes(password, dst, 0x3e8))
    {
        buffer4 = bytes.GetBytes(0x20);
    }
    return ByteArraysEqual(buffer3, buffer4);
}

答案 1 :(得分:36)

因为ASP.NET是开源的,所以你可以在GitHub上找到它:  AspNet.Identity 3.0AspNet.Identity 2.0

来自评论:

lapply(seq_along(my.vector), head, x = my.vector)

答案 2 :(得分:29)

我理解接受的答案,然后投了票,但我想在这里抛弃我的外行答案......

创建哈希

  1. 使用该功能随机生成盐 Rfc2898DeriveBytes ,它会生成哈希和盐。输入 Rfc2898DeriveBytes 是密码,要生成的salt的大小以及要执行的散列迭代次数。 https://msdn.microsoft.com/en-us/library/h83s4e12(v=vs.110).aspx
  2. 然后将盐和哈希一起捣碎(首先是盐) 通过散列)并编码为一个字符串(所以salt编码在 哈希值)。然后是这个编码的哈希(包含salt和hash) 存储(通常)在数据库中对着用户。
  3. 根据哈希检查密码

    检查用户输入的密码。

    1. 从存储的哈希密码中提取盐。
    2. salt用于使用 Rfc2898DeriveBytes 的重载对用户输入密码进行哈希处理,后者使用salt而不是生成一个。 https://msdn.microsoft.com/en-us/library/yx129kfs(v=vs.110).aspx
    3. 然后比较存储的散列和测试散列。
    4. 哈希

      在掩护下,使用SHA1哈希函数(https://en.wikipedia.org/wiki/SHA-1)生成哈希。 该函数被迭代调用1000次(在默认的Identity实现中)

      为什么这是安全的

      • 随机盐意味着攻击者无法使用预先生成的表 尝试破坏密码的哈希值。他们需要生成一个 每个盐的哈希表。 (在这里假设黑客也泄露了你的盐)
      • 如果2个密码相同则会 有不同的哈希。 (意思是攻击者不能推断'共同' 密码)
      • 迭代调用SHA1 1000次意味着 攻击者也需要这样做。这个想法是,除非他们有 在超级计算机上的时间,他们将没有足够的资源来暴力 从哈希强制密码。它会大大减慢为给定盐生成哈希表的时间。

答案 3 :(得分:7)

For those like me who are brand new to this, here is code with const and an actual way to compare the byte[]'s. I got all of this code from stackoverflow but defined consts so values could be changed and also

> grunt uncss cssmin

In in your custom ApplicationUserManager, you set the PasswordHasher property the name of the class which contains the above code.