为散列密码字段使用什么数据类型和长度?

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

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

那么,如何在数据库中存储这些密码呢?

417146 次浏览

作为一个固定长度的字符串(VARCHAR(n)或MySQL调用它)。 哈希总是有一个固定的长度,例如12个字符(取决于您使用的哈希算法)。因此,一个20字符的密码将被简化为12字符的哈希,一个4字符的密码也将生成12字符的哈希

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

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

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

这取决于你使用的哈希算法。如果我没记错的话,密码的长度和散列的长度没有什么关系。查找您正在使用的哈希算法的规格,运行一些测试,并截断上面的部分。

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

更新:简单地使用哈希函数存储密码是不够强大的。你应该阅读Gilles在这个帖子上给出的答案来获得更详细的解释。

对于密码,使用密钥强化哈希算法,如Bcrypt或Argon2i。例如,在PHP中,使用password_hash()函数,它默认使用Bcrypt。

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

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

$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位哈希值。你可能需要CHAR(56), CHAR(60), CHAR(76), BINARY(56)或BINARY(60)

截至2015年,NIST 建议使用SHA-256或更高版本用于任何需要互操作性的哈希函数应用。但是NIST不建议使用这些简单的哈希函数来安全地存储密码。

较小的哈希算法有它们的用途(比如应用程序内部,而不是用于交换),但它们是很容易被破解

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

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

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

Argon2赢得了2015年密码哈希比赛。ScryptbcryptPBKDF2是较老的算法,现在被认为不太受欢迎,但仍然是基本健全的,所以如果你的平台还不支持Argon2,现在可以使用另一种算法。

不要将密码直接存储在数据库中。也不要加密:否则,如果您的站点被攻破,攻击者就会获得解密密钥,从而获得所有密码。密码必须为散列

密码散列具有不同于哈希表哈希或加密哈希的属性。不要在密码上使用普通的加密散列,如MD5、SHA-256或SHA-512。密码哈希算法使用,它是唯一的(不用于任何其他用户或任何其他数据库)。salt是必要的,这样攻击者就不能预先计算普通密码的哈希值:使用salt,他们必须重新启动每个帐户的计算。密码哈希算法是本质上慢——你能承受的最慢的速度。慢速对攻击者的伤害比你大得多,因为攻击者必须尝试许多不同的密码。有关更多信息,请参见如何安全地哈希密码

密码哈希编码四段信息:

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

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

$algorithm$parameters$salt$output

where algorithm is a number or a short alphanumeric string encoding the choice of algorithm, parameters is a printable string, and salt and output are encoded in Base64 without terminating =.

16 bytes are enough for the salt and the output. (See e.g. recommendations for Argon2.) Encoded in Base64, that's 21 characters each. The other two parts depend on the algorithm and parameters, but 20–40 characters are typical. That's a total of about 82 ASCII characters (CHAR(82), and no need for Unicode), to which you should add a safety margin if you think it's going to be difficult to enlarge the field later.

If you encode the hash in a binary format, you can get it down to 1 byte for the algorithm, 1–4 bytes for the hardness (if you hard-code some of the parameters), and 16 bytes each for the salt and output, for a total of 37 bytes. Say 40 bytes (BINARY(40)) to have at least a couple of spare bytes. Note that these are 8-bit bytes, not printable characters, in particular the field can include null bytes.

Note that the length of the hash is completely unrelated to the length of the password.