使用 HMACSHA1 算法的散列密码的最大长度是多少

What is the maximum length of hashed passwords using the HMACSHA1 algorithm

我想在将密码存储到数据库之前对其进行哈希处理。有很多关于如何散列密码的示例,以下 C# 代码 from the docs 依赖于 HMACSHA1 算法:

public static void Main(string[] args)
{
    Console.Write("Enter a password: ");
    string password = Console.ReadLine();

    // generate a 128-bit salt using a secure PRNG
    byte[] salt = new byte[128 / 8];
    using (var rng = RandomNumberGenerator.Create())
    {
        rng.GetBytes(salt);
    }
    Console.WriteLine($"Salt: {Convert.ToBase64String(salt)}");

    // derive a 256-bit subkey (use HMACSHA1 with 10,000 iterations)
    string hashed = Convert.ToBase64String(KeyDerivation.Pbkdf2(
        password: password,
        salt: salt,
        prf: KeyDerivationPrf.HMACSHA1,
        iterationCount: 10000,
        numBytesRequested: 256 / 8));
    Console.WriteLine($"Hashed: {hashed}");
}

我想知道是否有一种方法可以根据密码的长度来确定密码哈希的长度。那么如果用户的密码长度为 x 有没有办法计算散列的长度?

我想知道它,因为在我的数据库中,密码列目前是一个 128 个字符的 varchar。建立在它之上的 REST API 应该限制密码长度,这样数据库就不会因为密码太长而崩溃并生成超过 128 个字符的密码哈希。

或者说数据库列是 256 个字符的 varchar 并且 API 只允许小于或等于 30 个字符的密码所以它永远不会达到限制是不是最佳做法?

如果答案独立于代码语言就好了,这更像是一个一般性的问题。

可以指定PBKDF2的输出。 PBKDF 是基于密码的密钥派生函数。通常那些有一个密钥扩展阶段,允许指定输出。

但是,如果 PBKDF2 用作密码哈希而不是用于密钥派生,则保留配置的哈希大小;提供可以从算法中检索到的最大安全性。在这种情况下,SHA-1 生成 160 位/20 字节。

除非你真的需要文本,否则输出可以存储为 20 字节的静态二进制文件。在您的情况下,您应该将其存储为 20 个字节的 base 64 版本。这应该相当于一个固定的 28 个字节:((20 + 2) / 3) * 4 = 28 来计算 base 64 扩展。但是,您的代码明确指定输出大小为 256 / 8 = 64 字节。快速计算表明它始终使用 88 个 base 64 字符来表示该大小。

在使用 SHA-1 时生成 64 个字节不是一个好的设置,因为它需要 PBKDF2 的内部函数 运行 4 次,让你没有任何优势 运行 只用它一次来产生 20 个字节,为攻击者提供优势。攻击者只需检查前 20 个字节以确保密码匹配,毕竟,为此只需要四个 运行 中的一个。 PBKDF2 用于扩展密钥大小超过哈希大小的方法确实效率低下,可能被认为是设计缺陷。

另一方面,10.000 次迭代不是很高。对于 PBKDF2,你应该:

  1. 指定底层散列的输出大小作为输出大小(20 字节而不是 SHA-1 的 64 字节)和
  2. 使用更多的迭代次数(受限于您可以在 PBKDF2 中花费的 CPU 时间)。

密码的大小对密码散列的大小没有任何影响。


请注意,其他 运行 上的一些密码哈希会自己创建密码哈希字符串,与 Unix 系统上的 crypt 更兼容。所以他们会有更大的输出,不能直接兼容。