http://www.cnblogs.com/loveis715/p/4417526.html
而后又出现了更多更为高明的方法,例如XSS,CSRF,Session Fixation等等。而相应的安全方法也逐渐地进行着改进,如HttpOnly Cookie,HTTP请求中添加的Referer头等。这一系列攻防所围绕的正是用户的身份凭证,如Session ID,用户的密码。因为有了它们,黑客就可以伪装成合法用户来悄悄地执行一系列非法行为,从而获得利益。
因此如何安全地保护用户的身份凭证实际上是各个网站最需要关注的问题。在使用身份凭证的任何地方,我们都需要提供对身份凭证的保护,如用户输入密码时,用户在登陆以后对网站的操作,身份凭证在网络上的传输,以及密码在数据库中的保存等等。而在这些场景中,对身份凭证进行保护的方法也不尽相同。
在用户输入密码的时候,黑客可以通过侦听用户输入来完成用户名/密码对的窃取。在用户登录以后,如果Cookie中记录了用户名/密码对,那么恶意人员也可以通过XSS(祥见我另一篇博客),CSRF等方式进行攻击。而身份凭证在网络上传输的时候,恶意人员可以通过中间人方式来窃取用户的身份凭证。可以看到,为了保证用户身份凭证的安全,一个网站需要在不同的维度对用户的身份凭证进行保护。
简单地想象下面的一个情况,那就是网站有SQL注入漏洞。那么恶意攻击者非常有可能通过该SQL注入漏洞来获得一系列用户名和简单哈希算法所加密过的密码。这些数据可能有上万个,甚至有百万条之多。现在他所知道的仅仅是密码经过哈希后的结果,而哈希过程中所使用的哈希算法以及输入他并不了解。
接下来,他要做的工作就是列出一系列可能的加密算法以及一系列最常用的密码。这里你需要相信的是,你所能想到的哈希算法一个有经验的黑客绝对能够想到。而常用的密码,上网上搜索“最常用的密码”,或者找一本密码字典就足够了。在一个包含了上万用户的网站中,有接近百分之百的几率出现形如“12345qwer”这样的密码。
OK,现在他的工作开始了:从密码字典中选择一个最常用的密码,并通过他所搜集到的哈希算法分别进行加密。接下来,将这些加密结果与他刚刚所得到的那些哈希过的密码进行比较。该过程中,一次哈希计算所得到的结果可以与其所得到的多个密码的哈希值同时进行比较。在样本数目非常大的情况下,该结果的命中概率将变得非常大。这就是攻击者在执行攻击时的一个非常大的优势:基于极多的样本并行地执行攻击尝试。
一旦一个哈希算法所得到的结果和任意一个密码的哈希值匹配成功,那么当前所使用的加密算法就非常有可能是系统所使用的加密算法。在使用不同的常用密码多次尝试以后,你所使用的加密算法在黑客眼中就已经非常明显了。如果这个加密算法是双向的,那么可以说,这个被攻击的系统已经完全沦陷了。
而这种攻击甚至可以被加速:在尝试猜测加密算法之前,该恶意用户会首先对这些常用的算法以及常用密码计算对应的哈希值,并直接使用这些哈希值与所得到的哈希过的密码进行比较。这甚至省略了计算哈希的时间,并使得这些哈希计算结果可以被重用。这种攻击有一个特殊的名字:Rainbow Table Attack。
现在我们来想想这种攻击成功的必要条件:一个黑客能够猜中的加密算法,和一个黑客同样能够猜中的密码。这两个必要条件都是基于概率的:黑客猜中加密算法的概率较高,而且在用户数目较大的情况下,系统中存在形如“12345qwer”的密码的概率非常大。而要想阻止黑客攻击,我们就需要让我们的网站不再具有这种必要条件。
对于第一个必要条件,我们的解决方案并不是要自创一个新的加密算法。这从安全的角度来说是完全不安全的。在后面的章节中我们会对这种做法为什么不安全进行讲解。而我们所需要的解决方案则是让黑客猜不中我们所使用的加密算法,也就是使用多种加密算法进行加密。这样即使是同样的密码也会产生不同的结果,减小黑客猜中哈希算法的概率。而对于第二个必要条件,我们则可以对密码本身进行一次增强。该增强算法需要尽量增加密码本身的复杂度却基本不产生密码碰撞(即增加了复杂度的两个密码最终变成了一样的密码)。这样即使黑客猜对了增强后的密码以及对其加密所使用的算法,那么他也无法知道原始密码到底是什么样子的。而这一步,业内的建议也是要由标准类库来完成。这其中的顾虑实际上与不要自创加密算法一样。
那么黑客就剩下一个攻击方法了,那就是硬猜,也就是常说的暴力破解(Brute Force Attack)。一个一个地猜虽然是一个笨方法,但是随着猜测次数的增加,猜中密码的概率也会逐渐增大。为了避免这种攻击成功,我们需要防止黑客快速地计算密码所对应的哈希值。一个简单的哈希函数,如MD5,在一个现代的设备上可以每秒运行上百万次,甚至上亿次。也就是说,如果我们使用一个简单的哈希函数,那么在一秒钟内可以有成千上万个哈希值参与比较。结果就是几十秒钟之内用户所使用的密码就可能被猜测出来了。因此对密码进行加密的方法需要较为缓慢,以增加这种攻击的难度。
但是呢,黑客手中还有一个利器,那就是并行计算。现在,构建一个可以进行并行计算的系统已经不再那么昂贵,甚至只需要一个支持并行计算的普通的GPU。因此就算是计算一次密码的哈希值较为缓慢,黑客可以通过同时计算多个密码所对应的哈希值进行加速,使得暴力破解的速度几十倍地增长。解决方法很简单:选择一个不支持并行计算的哈希算法。
OK,现在看来,我们已经对黑客所常用的攻击方法进行了防御。那么就让我们来总结一下进行加密的哈希算法所需要拥有的特征:
- 哈希算法需要是单向的。因为一旦使用了双向哈希算法,那么通过反向计算得到的字符串常常只包含数字,字母以及常用的符号。这在黑客眼中是一个非常明显的哈希算法猜测(接近)成功的信号。接下来,他只需要对各个哈希值反向计算即可得到相应的密码。
- 哈希算法的碰撞需要尽量少。因为如果N个不同的密码能够产生同一个哈希值,那么它被攻破的概率就大了N - 1倍。
- 减慢哈希的计算速度。这不仅仅需要减慢哈希的计算速度,还需要令哈希不支持并行计算。
- Salt。Salt就是我们刚刚提到的用来在加密系统中用于选择哈希函数并增强密码的组成。
现在我们来想想这种攻击成功的必要条件:一个黑客能够猜中的加密算法,和一个黑客同样能够猜中的密码。这两个必要条件都是基于概率的:黑客猜中加密算法的概率较高,而且在用户数目较大的情况下,系统中存在形如“12345qwer”的密码的概率非常大。而要想阻止黑客攻击,我们就需要让我们的网站不再具有这种必要条件。
OK,现在看来,我们已经对黑客所常用的攻击方法进行了防御。那么就让我们来总结一下进行加密的哈希算法所需要拥有的特征:
- 哈希算法需要是单向的。因为一旦使用了双向哈希算法,那么通过反向计算得到的字符串常常只包含数字,字母以及常用的符号。这在黑客眼中是一个非常明显的哈希算法猜测(接近)成功的信号。接下来,他只需要对各个哈希值反向计算即可得到相应的密码。
- 哈希算法的碰撞需要尽量少。因为如果N个不同的密码能够产生同一个哈希值,那么它被攻破的概率就大了N - 1倍。
- 减慢哈希的计算速度。这不仅仅需要减慢哈希的计算速度,还需要令哈希不支持并行计算。
- Salt。Salt就是我们刚刚提到的用来在加密系统中用于选择哈希函数并增强密码的组成。
Salt简介
相信读者对上面所提到的Salt不是很理解。例如:Salt中包含的值是什么?如何使用?存储在哪里?
一个比较受欢迎的生成Salt的方法就是得到一个128位或更长的随机整形数据并将其转化为字符串,或者是使用随机生成的UUID。在用户第一次设置其所使用的用户名和密码的时候,系统将为其生成一个Salt,并使用该Salt以及系统的加密方法计算用户密码的哈希值,并将该哈希值及Salt存在数据库中。而在用户再次登陆的时候,系统将根据用户所输入的密码以及之前为用户所生成的Salt再次使用系统的加密方法计算哈希值,并将计算结果与数据库内所保存的哈希值比较。如果两个哈希值相等,那么就表示用户所输入的密码是正确的,并登陆成功。
因为每次对密码的操作都会用到这个Salt,因此我们常常将它与用户名/密码对同时存储在数据库中。在加密过程中,Salt也将会作为我们所使用的加密方法的一个输入,以用来选择加密方法中所使用的哈希函数,并增强用户所使用的密码。从而使得对一系列密码的字典攻击以及Rainbow Table攻击失效。
或许你还是不是非常理解它是如何使字典攻击及Rainbow Table攻击失效的。那么我们假设现在一个黑客已经拿到了一系列的用户名/密码哈希值组合。在攻击时,他首先选取一个可能的密码password,并使用选定的哈希函数hash()进行加密操作hash(password),并与所有的密码哈希值进行比较。一旦成功,那么就基本上能确定选对了哈希算法。
而如果哈希过程中使用了Salt,那么他所得到的信息就是用户名/密码哈希值/Salt。由于每个用户的Salt并不相同,因此他需要根据各个用户的Salt值来计算哈希值,即hash(salt[0], password),hash(salt[1], password)等等。这使得通过一次计算就可以和多个哈希值进行比较变得不再可能。又由于哈希算法较为缓慢,因此黑客成功攻击所需要的时间便大大增加。
而如果哈希过程中使用了Salt,那么他所得到的信息就是用户名/密码哈希值/Salt。由于每个用户的Salt并不相同,因此他需要根据各个用户的Salt值来计算哈希值,即hash(salt[0], password),hash(salt[1], password)等等。这使得通过一次计算就可以和多个哈希值进行比较变得不再可能。又由于哈希算法较为缓慢,因此黑客成功攻击所需要的时间便大大增加。
前面已经说过,Rainbow Table攻击是通过提前计算各个可能密码的哈希值来缩短时间的。而现在参与加密的Salt则是一个随机字符串,如“js98LP6h”,显然Rainbow Table中所列出的可能的密码将不会包含这种形式的密码,从而使得Rainbow Table攻击失效。
对Salt的一个常见误区就是对Salt的使用可以增加破解单个密码的难度。其实并不然。一般情况下,Salt都和哈希过的密码一样存在于数据库中。因此恶意人员在访问到数据库中所记录的用户名/密码哈希值对的同时也能访问到哈希所需要使用的Salt。因此在尝试攻击时,其可以直接使用他所得到的Salt和密码字典中列出的各可能密码结合在一起计算哈希值。对这种攻击的防御是通过减慢哈希计算速度来完成的,而Salt则是用来防御并行攻击,即一次计算哈希就可以和多个哈希进行比较。
选择合适的加密方法
实际上,业内已经有很多用来对密码进行加密的方法了,如PBKDF2,bcrypt,scrypt等。这些加密方法各有优劣。因此在需要保护用户的密码时,我们需要尽量从这些标准加密方法中选择。在使用这些加密方法时,您还需要指定迭代次数等众多参数。这些参数对于网站本身来说都是机密,因此不要将它们存在数据库中,以免在数据库泄露的时候同时丢失这些设置,进而导致这些加密算法的使用细节泄露,减弱加密方法的安全性。
一个比较容易让人产生疑惑的就是加密算法中的碰撞。我们常常说MD5已经不再认为是安全的加密算法了。这是因为恶意人员可以很容易地找到一系列输入,使它们所产生的MD5是相同的。这在一系列验证领域中是不安全的,如文件的校验。因为在进行MD5校验的时候,恶意软件可以通过使它的MD5与目标文件相等而绕过MD5校验。但是密码的加密算法所要求的则是在经过加密后不能逆向解析出被加密的密码,因此它仍然是一种安全的加密算法。只是由于其计算速度过快,因此不建议被单独使用。