【问题标题】:What data type to use for hashed password field and what length?散列密码字段使用什么数据类型和长度?
【发布时间】:2010-09-19 19:47:38
【问题描述】:

我不确定密码哈希是如何工作的(稍后会实现),但现在需要创建数据库架构。

我正在考虑将密码限制为 4-20 个字符,但据我了解,加密后的哈希字符串长度会有所不同。

那么,如何将这些密码存入数据库呢?

【问题讨论】:

  • 另见 Openwall 的 PHP password hashing framework (PHPass)。它的便携性和强化了针对用户密码的一些常见攻击。编写框架 (SolarDesigner) 的人与编写 John The Ripper 并在 Password Hashing Competition 担任评委的人是同一个人。所以他对密码攻击略知一二。
  • 请不要设置密码上限。您正在对它们进行散列,没有上限的存储原因。如果您担心使用密码哈希的 DoS 攻击,1000 或 1024 是一个合理的上限。
  • 为什么要限制密码长度?至少让用户创建一个 100 个字符的密码 :)
  • 4 个字符对于密码来说是一个非常危险的下限,因为这些密码很容易破解。至少使用 8 但 14 或 16 更好。
  • 这是一个非常古老的问题,答案已经过时。请参阅 Gilles answer 了解最新信息。

标签: mysql hash types passwords cryptography


【解决方案1】:

作为一个固定长度的字符串(VARCHAR(n) 或者 MySQL 调用它)。 散列始终具有固定长度,例如 12 个字符(取决于您使用的散列算法)。所以一个 20 字符的密码会减少到一个 12 字符的哈希值,而一个 4 字符的密码也会产生一个 12 字符的哈希值。

【讨论】:

  • '或者 MySQL 称它为' - MYSQL 称它为 CHAR。此类型用于固定长度值。所以我认为 CHAR 是比 VARCHAR 更好的类型。
【解决方案2】:

我一直在测试找到加密字符串的最大字符串长度,并将其设置为 VARCHAR 类型的字符长度。根据您将拥有多少条记录,它确实有助于数据库大小。

【讨论】:

    【解决方案3】:

    您可能会在 Wikipedia 上找到有关盐渍 worthwhile 的这篇文章。这个想法是添加一组数据来随机化你的哈希值;如果有人未经授权访问密码哈希,这将保护您的密码免受字典攻击。

    【讨论】:

    • 这确实非常值得(+1),但它没有回答问题! (-1)
    • 是的,但在这种情况下绝对相关 (+1)
    【解决方案4】:

    您实际上可以使用CHAR(散列长度) 为 MySQL 定义您的数据类型,因为每个散列算法将始终计算出相同数量的字符。例如,SHA1 始终返回一个 40 个字符的十六进制数字。

    【讨论】:

    • SHA-1 不适合散列密码。
    【解决方案5】:

    这实际上取决于您使用的散列算法。如果我没记错的话,密码的长度与哈希的长度几乎没有关系。查看您正在使用的散列算法的规范,运行一些测试,然后在上面截断。

    【讨论】:

      【解决方案6】:

      哈希是位序列(128 位、160 位、256 位等,具体取决于算法)。如果 MySQL 允许,您的列应该是二进制类型的,而不是文本/字符类型的(SQL Server 数据类型是binary(n)varbinary(n))。你还应该给哈希加盐。盐可能是文本或二进制,你需要一个对应的列。

      【讨论】:

      • 正义在这里是完全正确的 - MySQL 会将这些存储为数值,并且将使在此列上的搜索比进行字符串匹配更有效,但是盐不应该存储在加盐数据旁边的数据库中- 这消除了盐提供的安全性。
      • 盐是不是的秘密。 only 秘密是密码。只要确保每个新密码都有一个新的盐。每次用户更改他的密码时,系统都应该为该密码生成一个新的盐。盐应该是长且随机的,例如从加密安全的 PRNG 生成的 16 个字节。
      • @TonyMaro 不确定在 SQL 级别上匹配密码字符串是否是一个好策略。换句话说,您不应该在数据库中搜索密码,而是根据用户名检索用户并在代码中而不是 SQL 中比较密码。
      【解决方案7】:

      更新:仅使用散列函数不足以存储密码。您应该阅读the answer from Gilles on this thread 以获得更详细的说明。

      对于密码,请使用增强密钥的哈希算法,例如 Bcrypt 或 Argon2i。例如,在 PHP 中,使用 password_hash() function,默认使用 Bcrypt。

      $hash = password_hash("rasmuslerdorf", PASSWORD_DEFAULT);
      

      结果是一个类似于以下的 60 个字符的字符串(但数字会有所不同,因为它会生成唯一的盐)。

      $2y$10$.vGA1O9wmRjrwAVXD98HNOgsNpDczlqm3Jq7KnEd1rVAGv3Fykk1a
      

      使用 SQL 数据类型 CHAR(60) 存储此 Bcrypt 哈希编码。请注意,此函数不编码为十六进制数字字符串,因此我们不能轻松地将其解开以存储为二进制。

      其他哈希函数仍然有用,但不能用于存储密码,所以我将保留下面的原始答案,写于 2008 年。


      这取决于您使用的哈希算法。无论输入如何,散列总是产生相同长度的结果。通常将二进制散列结果用文本表示为一系列十六进制数字。或者您可以使用UNHEX() 函数将一串十六进制数字减半。

      • MD5 生成一个 128 位的哈希值。您可以使用 CHAR(32) 或 BINARY(16)
      • SHA-1 生成一个 160 位的哈希值。您可以使用 CHAR(40) 或 BINARY(20)
      • SHA-224 生成一个 224 位哈希值。您可以使用 CHAR(56) 或 BINARY(28)
      • SHA-256 生成 256 位哈希值。您可以使用 CHAR(64) 或 BINARY(32)
      • SHA-384 生成一个 384 位哈希值。您可以使用 CHAR(96) 或 BINARY(48)
      • SHA-512 生成一个 512 位的哈希值。您可以使用 CHAR(128) 或 BINARY(64)
      • BCrypt 生成一个依赖于实现的 448 位哈希值。 You might need CHAR(56), CHAR(60), CHAR(76), BINARY(56) or BINARY(60)

      截至 2015 年,NIST recommends using SHA-256 or higher 适用于任何需要互操作性的哈希函数应用。但 NIST 不建议使用这些简单的哈希函数来安全地存储密码。

      较小的散列算法有其用途(例如在应用程序内部,而不是用于交换),但它们是known to be crackable

      【讨论】:

      • @Hippo:请不要将用户名用作盐。为每个用户生成一个随机盐。
      • 是的,没有理由不将它存储在同一行中。即使攻击者可以访问您的数据库,他们也必须基于该盐构建彩虹表。这与简单地猜测密码一样多。
      • @SgtPooki:您需要另一列以明文形式存储盐。然后,您可以在用户输入密码时使用相同的盐对密码进行哈希处理,并将结果与​​存储在表中的哈希摘要进行比较。
      • 如果您将 salt 存储在同一个表(或具有相同访问权限的任何其他位置)中,则没有理由不使用用户名作为 salt,因为每个用户都是唯一的。但是,任何已知的盐都会使哈希在密码学上比没有已知盐的情况下更弱。盐只有在它也是未知的情况下才会增加价值。
      • 我不明白已知盐与未知盐的处理方式。如果您正在实施一个站点 - 测试密码的登录页面/脚本/服务需要知道盐。所以——你们“不知名的”盐倡导者——你是不是假设攻击者不知道登录过程的代码?否则 - 攻击者不会总是知道盐,无论它是随机的、唯一的、与散列密码一起存储还是分开存储?
      【解决方案8】:

      对于 md5 vARCHAR(32) 是合适的。对于那些使用 AES 的人来说,最好使用 varbinary。

      【讨论】:

      • MD5 和 AES 都不适合散列密码。
      【解决方案9】:

      为了向前兼容,您应该使用TEXT(存储无限数量的字符)。散列算法(需要)随着时间的推移变得更强大,因此这个数据库字段将需要随着时间的推移支持更多的字符。此外,根据您的迁移策略,您可能需要将新旧哈希存储在同一字段中,因此不建议将长度固定为一种类型的哈希。

      【讨论】:

        【解决方案10】:

        始终使用密码哈希算法:Argon2scryptbcryptPBKDF2

        Argon2 赢得了 2015 年密码哈希竞赛。 ScryptbcryptPBKDF2 是较旧的算法,现在被认为不太受欢迎,但从根本上来说仍然是合理的,所以如果您的平台还不支持 Argon2,现在可以使用其他算法。

        切勿将密码直接存储在数据库中。也不要对其进行加密:否则,如果您的网站遭到破坏,攻击者将获得解密密钥,因此可以获得所有密码。密码必须散列

        密码散列具有不同于散列表散列或加密散列的属性。切勿在密码上使用普通的加密哈希,例如 MD5、SHA-256 或 SHA-512。密码散列算法使用唯一的 salt(不用于任何其他用户或任何其他人的数据库)。盐是必要的,这样攻击者就不能只预先计算常见密码的哈希值:使用盐,他们必须为每个帐户重新开始计算。密码哈希算法本质上很慢 - 尽可能慢。缓慢对攻击者的伤害比对您的伤害更大,因为攻击者必须尝试许多不同的密码。如需更多信息,请参阅How to securely hash passwords

        一个密码哈希编码了四条信息:

        • 使用哪种算法的指示符。这对于agility 来说是必要的:加密建议会随着时间而变化。您需要能够过渡到新算法。
        • 难度或硬度指示器。该值越高,计算散列所需的计算量就越多。这应该是密码更改功能中的常量或全局配置值,但随着计算机速度的提高,它应该会随着时间的推移而增加,因此您需要记住每个帐户的值。有些算法只有一个数值,有些算法有更多参数(例如分别调整 CPU 使用率和 RAM 使用率)。
        • 盐。由于盐必须是全局唯一的,因此必须为每个帐户存储它。每次更改密码时都应随机生成盐。
        • 哈希本身,即哈希算法中数学计算的输出。

        许多库包含一对函数,可以方便地将这些信息打包为单个字符串:一个接受算法指标、硬度指标和密码,生成随机盐并返回完整的哈希字符串;一个将密码和完整的哈希字符串作为输入并返回一个布尔值,指示密码是否正确。没有通用的标准,但通用的编码是

        $算法$参数$salt$输出

        其中<em>algorithm</em> 是一个数字或一个短的字母数字字符串,用于编码算法的选择,<em>parameters</em> 是一个可打印的字符串,<em>salt</em><em>output</em> 以 Base64 编码而不终止 =

        16 字节对于 salt 和输出来说已经足够了。 (参见例如recommendations for Argon2。)以 Base64 编码,每个 21 个字符。其他两部分取决于算法和参数,但通常为 20-40 个字符。总共大约 82 个 ASCII 字符CHAR(82),不需要 Unicode),如果您认为以后很难扩大该字段,则应在其中添加安全边距。

        如果您以二进制格式对哈希进行编码,您可以将其缩减为算法的 1 个字节,1-4 个字节的硬度(如果您对某些参数进行硬编码),每个 16 个字节的salt 和 output,总共 37 个字节。说 40 字节 (BINARY(40)) 至少有几个备用字节。请注意,这些是 8 位字节,不是可打印字符,特别是该字段可以包含空字节。

        请注意,哈希的长度与密码的长度完全无关。

        【讨论】:

          猜你喜欢
          • 2011-08-18
          • 2012-12-19
          • 2011-02-21
          • 1970-01-01
          • 2011-10-29
          • 1970-01-01
          相关资源
          最近更新 更多