用户密码的存储校验问题小结

【很久前做的笔记,部分内容好像是直接拷贝了别人的文章,但年代久远难以引用,抱歉。】

哈希函数的选取

SHA256,SHA512,RipeMD和WHIRLPOOL

加密向的哈希函数与搜索向的哈希函数,安全性上有本质差异。

加盐工具

应避免固定盐值,改为随机生成,且盐值长度应至少与加密后的哈希结果长度相等。

使用时,既存储加盐的哈希结果,也存储盐值本身。

客户端哈希问题

  1. 客户端哈希同样需要加盐,很显然的办法就是向服务器请求用户的盐值,但是不要这么做。因为这给了坏蛋一个机会,能够在不知道密码的情况下检测用户名是否有效。既然你已经在服务端对密码进行了加盐哈希,那么在客户端把用户名(或邮箱)加上网站特有的字符串(如域名)作为盐值是可行的。
  2. 客户端密码哈希并不能代替HTTPS(SSL/TLS)。如果浏览器和服务器之间的连接是不安全的,那么中间人攻击可以修改JavaScript代码,删除加密函数,从而获取用户密码。
  3. 服务端要对客户端发来的哈希结果进行二次加密。

组合哈希

尽量避免使用组合哈希。

随机生成器

应使用基于加密的伪随机数生成器(Cryptographically Secure Pseudo-Random Number Generator – CSPRNG)代替标准的rand方法。

Env CSPRNG
PHP mcrypt_create_iv, openssl_random_pseudo_bytes
JAVA java.security.SecureRandom
.Net System.Security.Cryptography.RNGCryptoServiceProvider
Ruby SecureRandom
Python os.urandom
Perl Math::Random::Secure
C/C++ CryptGenRandom
GNU/Linux or Unix Read from /dev/random or /dev/urandom

存储密码的步骤

  1. 使用CSPRNG生成足够长度额盐值。
  2. 混合盐值和密码,使用哈希函数进行加密。
  3. 将哈希值与盐值同时存入数据库。

校验密码的步骤

  1. 从数据库取出哈希结果和盐值。
  2. 将盐值混如用户输入的密码,使用同样的哈希函数进行加密。
  3. 比较上一步的哈希结果是否与数据库中的相同。

慢哈希函数

比如PBKDF2或者bcrypt。

这类算法使用一个安全因子或迭代次数作为参数,这个值决定了哈希函数会有多慢。对于桌面软件或者手机软件,获取参数最好的办法就是执行一个简短的性能基准测试,找到使哈希函数大约耗费0.5秒的值。这样,你的程序就可以尽可能保证安全,而又不影响到用户体验。

HMAC问题

略。

时间计算攻击

举个例子,使用标准的方法比较“xyzabc”和“abcxyz”,由于第一个字符就不同,不需要检查后面的内容就可以马上返回结果。相反,如果比较“aaaaaaaaaaB”和“aaaaaaaaaaZ”,比较算法就需要遍历最后一位前所有的“a”,然后才能知道它们是不相同的。

攻击者可以基于这个原理,统计不同字符串的校验时间,以推算HASH结果。

//慢比较的样例
private static boolean slowEquals(byte[] a, byte[] b)
{
    int diff = a.length ^ b.length;
    for(int i = 0; i < a.length && i < b.length; i++)
    diff |= a[i] ^ b[i];
    return diff == 0;
}
// 使用了异或运算符“^”(XOR)来比较两个整数是否相等,而不是“==”。当且仅当两位相等时,异或的结果才是0。因为0 XOR 0 = 0, 1 XOR 1 = 0, 0 XOR 1 = 1, 1 XOR 0 = 1。应用到整数中每一位就是说,当且仅当字节两个整数各位都相等,结果才是0。

发表评论

邮箱地址不会被公开。 必填项已用*标注

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据