密码加盐哈希
读 Salted Password Hashing - Doing it Right 收获很大,之前有很多理解都是错误的。安全问题很难被测试出来,遵循安全规范是最佳策略。
本文主要讨论密码的安全,但有一些方法和原则是通用的。
1 不用自作聪明
柯克霍夫原则(Kerckhoffs' principle):安全不依赖于算法,只依赖于秘钥。所以不要试图实现自己的加密(哈希)算法,或者组合已有算法,试图通过算法的“私有”来抵御攻击。
2 密码哈希
哈希是一种单向函数, H(p) = q
,没有办法根据q计算出p,并且不同的p计算出的q不同。我们通过存储密码的哈希值,就算密码库泄露了,根据哈希值也无法获取密码,达到安全的目的。然而,并非如此。(主要还是密码太短了,且不够随机。如果密码长度都是1K以上的随机字符串,也很安全。)
3 哈希的破解
3.1 字典或暴力破解
通过算出常用密码表或者字符排列的哈希,对比密码库的哈希,如果命中,密码破解。这种方式效率太低,尤其是暴力破解。(借助GPU,短密码的破译问题不大)
3.2 查找表
预先计算出密码和哈希的对应表,对比哈希,找到密码。效率很高。大家在密码上的想象力太小了,整个密码空间并不大。
3.3 反向查找表
没看懂
3.4 彩虹表
一种优化的查表法,隐含存储的表更大,但实际用的空间缺不大,查找时耗费更多CPU。
4 加盐
查表法和彩虹表能工作是因为密码和密码的哈希是一一对应的。例如:sha256("iop3er") 的结果是 3fd23da8467ae302aec51d7d73e14924cfb9c5e6fbc0b6cd6764ea681d292aa0 ,这种对应让哈希值和密码等价了。 加盐 就是打破这种一一对应。例如:sha256("iop3er" + "Abt3dE") 的结果是 c668ba513909925384d766f6b8fd488055614fab6e34fa873ce29c12390ef389,sha256("iop3er" + "Px2I9g") 的结果是 7a14a66eaad6fa1d66ba540a82bfb808bf3dd216bc8c79ae3bffba2a144f0c5b。就算知道了 盐 和哈希值,因为密码和哈希值不再一一对应,破解难度大大增加。
4.1 加盐的误区
4.1.1 重复使用相同的盐
盐 的目的是增加不确定性,应该每个密码使用不同的盐,盐和密码哈希存放在一起。
4.1.2 盐的长度(空间)太短
盐的长度至少和哈希值一样长,例如用sha256哈希,盐至少32个字节,并且从所有可见ascii码中选取
5 慢哈希
加盐 也无法抵御暴力破解,借助先进的GPU和专门硬件,1秒可以计算数10亿哈希。为了降低破解的效率,可以使用 秘钥延伸 (key stretching)。 秘钥延伸 的核心是让哈希变慢,这样即使使用GPU或专门硬件破解也变的非常耗时(但不影响正常用户登陆体验)。常见的慢哈希算法有 PBKDF2
bcrypt
。
6 使用哈希key或者加密哈希
可以(DES)加密哈希或者使用Hmac(基于哈希的消息认证码)。不要把key和密码拼一起,然后做hash,要使用hmac。(我见过很多认证系统是将key和请求的其它信息拼一起做哈希,作为签名的),要注意哈希key或秘钥的保密。