如何存储密码(KDF)


类型:Python,C++ & Qt4,创建时间:八月 9, 2018, 11:11 p.m.

标题无“转载”即原创文章,版权所有。转载请注明来源:http://hgoldfish.com/blogs/article/108/。

在线系统经常需要存储用户名和密码等认证信息,以便在用户登录的时候认证密码。但存储密码的时候其实有很多学问。在原始的互联网时期,使用明文存储密码。这是非常不可取的,经历了多次严重的泄露事件以后,即使是最蠢的程序员都知道得在存储前使用 MD5 哈希一下。

但现在我们已经知道使用 MD5 存储也不靠谱了。拿着 MD5 哈希后后的一串代码放到搜索引擎里面搜一下(彩虹表攻击),密码就出来了。对抗这种攻击的办法是给密码加盐,就是每次哈希前都带上一段随机字符串,并且和密码一起存储起来,这大大提高了破解的难度,即使你使用的是 123456 这么简单的密码,也不容易一眼就看出来了。

如今,MD5/SHA1 哈希算法已经被攻破,即使加上盐以后也不再保险。所以换成 SHA256/SHA512 就不说了。但即使这样,拿着常用密码本对着泄露后的数据库进行破解也是相对容易的事情。

除了存储密码,使用人类可读的密码来加密信息的时候也需要考虑类似的反破解要求。在密码学里面,像上面介绍的,从人类可读的密码生成一段不容易被逆向的密码哈希,称为 Key Derivation Function,简称 KDF. 从 password 生成加密用的 key,所以才叫 Key Derivation.

目前最常用的 KDF 方案可能是 PBKDF2. 它的前身 PBKDF1 只能生成 最多160 位的 key,所以被废弃了。

PBKDF2 算法可以表示为一个调用:

DK = PBKDF2(PRF, password, salt, c, dk_len)

其中参数 PRF 表示使用何种哈希算法,password 是人类可读密码,salt 是随机生成的盐,c 是迭代次数,dk_len 是最终生成的 key 的长度。当 DK 用于加密时,dk_len 一般是加密算法要求的 key 长度,也就是块大小。

它的原理就是加盐以及增加哈希迭代次数。当黑客拿到密码库,因为每次破解的尝试都需要大量的计算,最终会让黑客的破解变得非常困难,无利可图。在最佳实践里面,建议使用以下参数的 PBKDF2 算法:

PRF = SHA256
c = 100000

随便说一下,django 默认使用以上参数来存储密码。所以有人说 django 可能是出厂设置最安全的 web 框架了。

除了 PBKDF2,openbsd 开发了另外一种常用的 KDF 算法,名为 bcrypt. 它基于 blowfish 的特点,通过增加每次迭代的计算开销,达到提升破解难度的目标。

最近几年比特币挖矿的发展,让大家看到专有硬件、GPU 在对付大规模哈希时的威力。像 PBKDF2 这样简单使用 SHA256,看起来已经不太保险了。为了应付专有硬件的攻击, scrypt 算法被发明出来,它通过使用大量的内存来提升 ASIC 硬件的制作难度。于是这种算法也被顺理成章地被某些加密货币使用。比如 Litecoin 就使用 scrypt 作为它的 POW 算法。但据说已经有挖矿机被制造出来了。

scrypt 算法同样表示为了一个调用:

DK = Scrypt(salt, dk_len, n, r, p)

其中的 salt 是一段随机的盐,dk_len 是输出的哈希值的长度。n 是 CPU/Memory 开销值,越高的开销值,计算就越困难。r 表示块大小,p 表示并行度。在最佳实践里面推荐使用以下参数:

n = 2 ** 14     // 当密码用于登录时或者存储时
r = 8
p = 1

或者:

n = 2 ** 20      //加密非常敏感的文件
r = 8
p = 1

scrypt 的内存消耗很厉害。以上参数n = 2 ** 20, r = 8时消耗的内存是:

128 * ( 2 ** 20) * 8 = 1073741824

也就是说消耗了 1G 内存

在 2015 年的”密码哈希大赛“里面,Argon2 算法胜出,成为最新的被推荐的 KDF 算法。这个算法可以使用多种参数控制内存使用、迭代次数、并行度等。不过可能是太新了,目前软件支持还不太好。在本文写作的时候(2018/07/16),openssl 仍然没有支持这个算法。

最后推荐一个 github 资源,里面有各种示例教大家使用 KDF 算法:

https://github.com/Anti-weakpasswords

标题无“转载”即原创文章,版权所有。转载请注明来源:http://hgoldfish.com/blogs/article/108/。


暂时还没有任何评论。


何不来发表一下您对本文的看法(使用Markdown语法,分段空两行):