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

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

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


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


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


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


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


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


哈希是一个比特序列(128位、160位、256位等,取决于算法)。如果MySQL允许,你的列应该是二进制类型的,而不是文本/字符类型的(SQL Server数据类型是二进制(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 generates a 128-bit hash value. You can use CHAR(32) or BINARY(16) SHA-1 generates a 160-bit hash value. You can use CHAR(40) or BINARY(20) SHA-224 generates a 224-bit hash value. You can use CHAR(56) or BINARY(28) SHA-256 generates a 256-bit hash value. You can use CHAR(64) or BINARY(32) SHA-384 generates a 384-bit hash value. You can use CHAR(96) or BINARY(48) SHA-512 generates a 512-bit hash value. You can use CHAR(128) or BINARY(64) BCrypt generates an implementation-dependent 448-bit hash value. You might need CHAR(56), CHAR(60), CHAR(76), BINARY(56) or BINARY(60)

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

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


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


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


始终使用密码哈希算法:Argon2, scrypt, bcrypt或PBKDF2。

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

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

A password hash has different properties from a hash table hash or a cryptographic hash. Never use an ordinary cryptographic hash such as MD5, SHA-256 or SHA-512 on a password. A password hashing algorithm uses a salt, which is unique (not used for any other user or in anybody else's database). The salt is necessary so that attackers can't just pre-calculate the hashes of common passwords: with a salt, they have to restart the calculation for every account. A password hashing algorithm is intrinsically slow — as slow as you can afford. Slowness hurts the attacker a lot more than you because the attacker has to try many different passwords. For more information, see How to securely hash passwords.

密码哈希编码四段信息:

An indicator of which algorithm is used. This is necessary for agility: cryptographic recommendations change over time. You need to be able to transition to a new algorithm. A difficulty or hardness indicator. The higher this value, the more computation is needed to calculate the hash. This should be a constant or a global configuration value in the password change function, but it should increase over time as computers get faster, so you need to remember the value for each account. Some algorithms have a single numerical value, others have more parameters there (for example to tune CPU usage and RAM usage separately). The salt. Since the salt must be globally unique, it has to be stored for each account. The salt should be generated randomly on each password change. The hash proper, i.e. the output of the mathematical calculation in the hashing algorithm.

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

$algorithm$parameters$salt$output

其中algorithm是一个数字或编码算法选择的短字母数字字符串,parameters是一个可打印的字符串,salt和output用Base64编码,不终止=。

对于盐和输出,16个字节就足够了。(参见Argon2的推荐。)用Base64编码,每个有21个字符。另外两部分取决于算法和参数,但20-40个字符是典型的。总共大约有82个ASCII字符(CHAR(82),不需要Unicode),如果您认为以后很难扩大字段,则应该在此基础上添加一个安全裕度。

如果您以二进制格式对哈希进行编码,则可以将算法的哈希编码为1个字节,硬度为1 - 4个字节(如果您对某些参数进行硬编码),盐和输出分别为16个字节,总共为37个字节。假设是40字节(BINARY(40)),至少有两个空闲字节。注意,这些是8位字节,不是可打印字符,特别是字段可以包括空字节。

注意,散列的长度与密码的长度完全无关。