web开发中的密码保护问题:HASH函数保护密码 彩虹表 MD5+salt bcrypt

哈希(Hash)算法就是单向散列算法,它把某个较大的集合P映射到另一个较小的集合Q中,假如这个算法叫H,那么就有Q=H(P)。对于P中任何一个值p都有唯一确定的q与之对应,但是一个q可以对应多个p。

作为一个有用的Hash算法,H还应该满足:H(p)速度比较快;给出一个q,很难算出一个p满足q=H(p);给出一个p1,很难算出一个不等于p1的p2使得H(p1)=H(p2)。

正因为有这样的特性,Hash算法经常被用来保存密码,我们使用相应的哈希算法对于密码整体P(注意是整体P),计算出它的q,并进行存放。而在密码比对时,使用输入的密码经过哈希函数算出的q和我们已经在存放的该用户的对应的密码经哈希算出的q来进行对比。

这样即使别人知道了q,很难由q推算出p。或者很难推算出一个p使得这个p经过哈希函数算出的q能够和原来的q匹配从而破解密码,————这样不会泄露密码明文,又可以校验输入的密码是否正确。常用的Hash算法有MD5、SHA1等。

但是需要注意有破解的方法,一种是暴力破解法,就是对于所有的密码情况,我们算出它的q,直到它等于q,这种方式需要计算相当长的时间。第二种就是使用内存换时间,我们先把所有的情况的q都计算出来,存放在一个Tables中,然后一个个去查询,这种方式需要相当大的存储空间。

现在还有第三张折中方法,彩虹表

彩虹表的根本原理就是组合了暴力法和查表法,并在这两者之中取得一个折中,用我们可以承受的时间和存储空间进行破解。它的做法是,对于一个Q=H(P),建立另一个算法R使得P=R(Q),然后对于一个p,这样进行计算:

p0-H->q1-R->p1-H->q2-R->p2-H->q3-R->p3…-H->q(n-1)-R->p(n-1)-H->qn-R->pn(这里的n使我们自己选取的,如果n很大,那么就代表我们可以用一个p0pn对记录更多的数据)

简单的说,就是把q用H、R依次迭代运算,最后得到pn,n可能比较大。最后我们把p0和pn都存储下来,把其他的结果都丢弃。然后用不同的p0代入计算,得到多个这样的p的对子。

我们在做破解的时候,给出了一个q,我们来寻找p。我们先把q做一次R运算得到一个值例如叫c1,然后把c1和每一个p对的最后一个做比较,假如和某一个pn相等,那么有可能这个pn往前推得到的p(n-1)就是我们在追寻的p,为了验证我们把pn对应的p0再做一次链式计算,比对qn是否就是我们之前给出的q,如果是,很明显p(n-1)就是我们在追寻的p,因为p(n-1)-H->qn。如果不是就继续寻找直到遍历所有的q0qn对。

事情还刚刚开始,我们再算q-R->c1-H->-R->c2,再比对c2是否是p(n-1),如果是,那么p(n-2)就可能是p,因为p(n-2)-H->q(n-1)-R->p(n-1)-H->qn-R->pn;再算c3、c4直到c(n-1).//该段话有待检验

所以为了阻止黑客们使用彩虹表破解密码,我们考虑使用MD5+salt的方式,这种方式的思路就是既然你可以得到我的密码散列值并通过散列表或是彩虹表破解我的密码,那么我只要我使用某种方式使你的散列表失效就可以了,这里就是给用户输入的明文密码加上一个随机字符串(这里的随机字符串就是salt,我们可以想象成作料)形成一个新的字符串,然后再散列。在存储经散列后的密码的同时也要将salt存储到对应的用户名中去,在下次用户输入密码验证时,就将密码和数据库中的salt相加再散列再和原来用户注册的密码散列值比对。详见

http://blog.sina.com.cn/s/blog_59a133f7010124ee.html

随着计算机的飞速发展,使用CUDA穷举所有情况,快速破解密码称为可能,那么我们该怎么办?我们需要一个不随计算机性能增长而加速的算法,这里就提出了bcrypt算法:

引用
因为bcrypt采用了一系列各种不同的Blowfish加密算法,并引入了一个work factor,这个工作因子可以让你决定这个算法的代价有多大。因为这些,这个算法不会因为计算机CPU处理速度变快了,而导致算法的时间会缩短了。因为,你可以增加work factor来把其性能降下来。

可以参考http://coolshell.cn/articles/2078.html