298

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

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

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

4

10 回答 10

473

更新:简单地使用散列函数不足以存储密码。您应该阅读Gilles 在此线程上的答案以获得更详细的解释。

对于密码,请使用增强密钥的哈希算法,例如 Bcrypt 或 Argon2i。例如,在 PHP 中,使用password_hash() 函数,该函数默认使用 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 位哈希值。您可能需要 CHAR(56)、CHAR(60)、CHAR(76)、BINARY(56) 或 BINARY(60)

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

较小的散列算法有其用途(如应用程序内部,而不是用于交换),但众所周知,它们是可破解的。

于 2008-10-29T17:12:11.177 回答
25

始终使用密码散列算法:Argon2scryptbcryptPBKDF2

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

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

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

密码哈希编码四条信息:

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

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

$算法$参数$$输出

其中algorithm是一个数字或一个短的字母数字字符串,编码算法的选择,parameters是一个可打印的字符串,并且saltoutputBase64 中编码而没有终止=

16 个字节足以用于盐和输出。(参见例如Argon2 的建议。)以 Base64 编码,每个 21 个字符。其他两部分取决于算法和参数,但通常为 20-40 个字符。总共大约有 82 个 ASCII 字符CHAR(82),并且不需要 Unicode),如果您认为以后很难扩大该字段,则应该在其中添加安全边际。

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

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

于 2019-04-18T21:07:12.677 回答
15

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

于 2008-10-29T15:53:24.207 回答
13

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

于 2008-10-29T15:50:12.667 回答
10

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

于 2008-10-29T15:47:20.287 回答
6

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

于 2017-07-25T22:32:03.343 回答
5

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

于 2008-10-29T15:59:51.307 回答
4

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

于 2008-10-29T15:54:33.750 回答
2

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

于 2008-10-29T15:48:06.477 回答
-1

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

于 2010-05-29T07:05:52.183 回答