CSDN数据库泄露!那些网站够安全吗?
近日, CSDN 社区网站数据库泄露 ,近 600 万用户真实账号密码外泄。该事件横扫整个中文互联网,并且随后又爆出 多玩游戏 800 万用户资料被泄露 ,另有传言人人网、开心网、天涯社区、世纪佳缘、百合网等社区都有可能成为黑客下一个目标。一时间人人自危,更换密码者无数。
这到底是怎么回事?用户信息是如何泄漏的?为什么一瞬间就有这么多密码被轻易获取,而一般的网站又是如何保护用户密码的?黑客和网站管理者之间,展开过怎样的密码攻防战?死理性派就此做了一番详细的介绍。
黑客的目标:你的密码存在哪儿?
很多人对这次事件都有一个疑问:网站把我的密码存在哪?
对于大部分网站,密码的存储和验证过程简单来说就是:用户输入密码,密码被传输到服务器,服务器将密码存储起来(注册)或和已经存储的密码比对(登录)。不论在哪个步骤,都有可能遭到黑客的攻击和入侵。本次事件的数据库泄露,就和第 3 个步骤相关。
一般来说,每个网站都有一个或多个专门存储密码的数据库。这些数据库通常并不能下载到。那黑客们是如何得到数据库的呢?
比较技术的方法是通过一些服务器返回的错误信息推断数据库的位置,例如“%5c大法”。它的原理是,故意进行一个错误的数据库访问,从错误信息中获取数据库真实地址,再通过这个地址下载数据库。但修补这个漏洞的方法也很简单,只要更改一下数据库出错时返回的错误信息就可以了。
有经验的软件管理员自然会对这种技术手段有所防范。但密码泄漏的途径其实不像想象中的那么“技术”:可能某个辞职员工偷偷带走了数据库;可能某台存储数据库的服务器染上了病毒;可能有人偷偷潜入了机房拷贝走了数据……总之,黑客会通过各种可能的手段获得存有用户名和密码的数据库,数据库泄露的危险总是存在的。
图片来源:XKCD
道高一尺:加密让数据库更安全
数据库总有被黑客攻击的危险,那网站如何应对?众所周知,在本次事件当中,被泄露的 CSDN 数据库,用的是明文储存密码。顾名思义,明文密码就是直接将用户输入的密码存到数据库中。这种方式的安全性不言自明。
既然明文密码太危险,那不妨用加密密码。但怎么加密呢?这就要说到大名鼎鼎的哈希函数了。
哈希是一种从一到多(或从多到一)对应的函数,它的作用是将一个很大的集合映射到一个很小的集合。例如,我们可以设计一个哈希函数 h(x) = x % 100 (其中 % 表示取余数,如 4 % 3 = 1,8 % 2 = 0),这样我们就把所有的整数 x 映射到了一个 0 到 99 的有限集合中。而像 MD5 和 SHA-1 这些著名的算法,都是哈希函数。当然,和前面这个 h(x) 不同,它们的计算方法非常复杂,常常需要经过很多轮计算。
哈希一个很重要的作用是帮助查找。很容易看到,如果两个数 x 1 和 x 2 对应的哈希值 h( x 1 ) 和 h( x 2 ) 不相等,那么 x 1 和 x 2 也一定不相等。这样一来,我们就可以用两个对象的哈希值进行过滤。因为哈希值的集合比原始值的集合更小,所以比较哈希值的速度是很快的。
典型的哈希算法。 图片来源:维基百科
而这个想法,也被应用于密码的保护上。实际上,无论是避免数据库泄露后的危险,还是出于对用户隐私保护的需要,现在大部分网站数据库存储的密码都不是明文,而是用户密码的哈希值,也就是所谓的加密密码。如果输入了一个错误的密码,它的哈希值就会有很大可能性与正确密码的哈希值不同,那么用哈希值代替密码进行比较也没有什么问题。另外,哈希的作用是将一个大集合映射到一个小集合,所以它的结果一定是不可逆的(因为从小集合到大集合的映射是一对多的映射)。这种不可逆性,使得理论上从哈希值找出原始密码成了一个不可能的任务,从而保护了密码。
但是,也会出现这种情况:两个不同的原始密码,它们的哈希值一样。这在密码学上被称作“碰撞”。碰撞是一个设计良好的哈希函数需要极力避免的,正因为如此,很多哈希函数的运算结果都很长,例如知名的 MD5 算法,它的运算结果包括了 128 位二进制数。试想一下,如果使用之前提到的哈希函数 h(x) = x % 100,随便输入一个密码,就有 1/100 的可能性验证通过,那还了得!好在现在流行的哈希函数,在这一点上做的还都还不错。
魔高一丈:哈希不是万能的
前面提到,哈希是不可逆的。那么使用哈希存储的密码,真的就安全了吗?如果你当真这么认为,那黑客就要在一旁偷偷地笑了——非也,非也!
事实上,即使数据库中的密码经过了哈希函数的加密,数据库泄露之后也会有很大的危险,尤其是当使用的哈希算法还是 MD5 和 SHA-1 这种已经流行很多年的“大众脸”时。确实,哈希本身并不可逆,但要注意的是,黑客并没多少兴趣知道原始密码:因为服务器验证密码时不需要原始密码。只要找到一个和数据库里存储的哈希值相同的密码,就可以用它轻松通过验证,这正是前面提到哈希函数要尽力避免碰撞的原因。
通常黑客手中,都会有一份很长的列表,称为“碰撞库”。碰撞库里存储的,是很多常用密码对应的各种哈希值。这里的“常用”,并不单单是指 “123456789” 这种“烂大街”的密码——事实上,任何不够长、只含字母或数字的类似于 “iloveyou” 这种由简单单词组成的密码,都可能出现在碰撞库中,现在黑客们生成的碰撞库的规模早已超出我们的想象。对于大部分常用密码,只要把泄漏出的哈希值放到碰撞库中比对一下,就可以找到一个产生碰撞的密码。
而滑稽的是,哈希算法极力要避免的碰撞在这里又给了黑客很大的方便:因为碰撞几率极低,如果一个哈希值存在于碰撞库里,那基本就可以确定库中对应的碰撞密码正是经过哈希加密前的真实密码!因此,那些用户名密码在各个网站都一成不变的用户们就悲剧了。
并非束手无策:猜不出的哈希算法
即便如此,网站管理员也并非束手无策。黑客可以用列表来破解哈希后的密码,很大程度上是因为加密时使用的哈希算法是公开的。如果黑客不知道加密的哈希算法是什么,那他也就无从下手了。
一个直接的解决办法是,自己设计一个哈希算法。然而,一个好的哈希算法是很难设计的——既要避免碰撞,又不能有明显的规律,做到这两点要比想象中的要困难很多。因此实际应用中更多的是利用已有的哈希算法进行多次哈希。
但是单纯的多次哈希,依然阻挡不住黑客。两次 MD5、三次 MD5之类的方法,我们能想到,黑客自然也能想到。特别是对于一些开源代码,这样哈希更是相当于直接把算法告诉了黑客。
没有攻不破的盾,但也没有折不断的矛。现在安全性比较好的网站,都会用一种叫做“加盐”的方式来存储密码,也就是常说的 “salt”。他们通常的做法是,先将用户输入的密码进行一次MD5(或其它哈希算法)加密;将得到的 MD5 值前后加上一些只有管理员自己知道的随机串,再进行一次 MD5 加密。这个随机串中可以包括某些固定的串,也可以包括用户名(用来保证每个用户加密使用的密钥都不一样)。举个例子:
用户名:sqybi 密码:12345678 先对 12345678 进行第一次 MD5,得到:25d55ad283aa400af464c76d713c07ad 指定两个 salt: salt1 = @#$% salt2 = ^&*() salt1+用户名+salt2+MD5,得到:@#$%sqybi^&*()25d55ad283aa400af464c76d713c07ad 对新字符串再计算一次 MD5,得到最终的加密串:7edfee82ab9b6f5aa50edbc3a9eb6507
在两个 salt 没有泄露的情况下,黑客如果拿到的是最后这个加密串,就几乎不可能推算出原始的密码是什么了。
最后的安全攻防:数据库泄露之外的危险
但是,即使有这种安全的密码存储方式,也不能太掉以轻心。在文章的一开始我们就说过,密码的存储和验证有三个步骤,每个步骤都有可能被黑客趁虚而入,数据库泄露只不过是其中的一个罢了。
在用户输入密码这个步骤,黑客可以通过植入木马等方式直接监视用户的键盘操作;在密码被传输到服务器这个步骤,黑客可以设法截获数据包分析其中包含的密码。而即使数据库有“加盐”,设置过于简单的密码也不是好选择——salt的值也不是没有泄露的可能!
要说的是,无论对于用户还是网站管理员,有良好的安全习惯才是最好的防御手段。用户应经常查毒,了解每个系统进程的作用,让木马没有藏身之处;网站管理员可以通过提供https连接或者在本地对密码进行首次加密再传输给服务器,避免数据包被截获;而经常更换密码,重要的账号不重复使用同一个密码,都可以在密码泄露的时候把损失降到最低。关于用户应当如何对待自己密码这个问题,死理性派的 你的密码安全吗?小心那些隐藏的陷阱 有过全面而详细的介绍,强烈推荐大家一读。
总之,网络的时代想保护好自己的隐私,需要谨慎、谨慎再谨慎,养成良好的安全习惯,才是保护密码的最好方式。而至于网络安全要走的路,还有很长、很长……
参考资料:
相关阅读:
[1] 开源密码管理生成器
[2] 怎样设置并记住好记又难猜的密码?
这几天你收到多少封“XXX提醒您注意密码安全”的邮件了?