最近在做一个小系统,需要用户登录功能。虽然系统对安全性要求并不高,但是希望借此机会增长下安全方面的知识,因为网络信息安全对于编程甚至对日常生活都很重要。于是就搜索一下网上关于登录密码安全防护的一些网页,在这里进行一下整理和总结。由于我对安全方面的知识只有较粗浅的了解,因此这里给出了觉得比较有参考价值的文章的链接,请想深入了解的人还是亲自参考下那些文章,或者延伸阅读更专业、详尽的书籍、资料。另外,我在查资料的过程中曾经看过一个CSDN上的帖子,楼主说的内容大约是在询问有什么方法能够保证密码的安全,而底下的大多回复却在抨击这种想法,觉得密码安全无法得到完全解决,考虑这个问题太耗费开发精力,使用明文传输密码也没什么大不了反正新浪邮箱就用的明文云云。窃以为这种想法不可取。我并不否认某些应用中并不需要过高的安全级别,过多考虑安全问题反而会消耗开发成本(其实我所开发的项目大约也是如此)。但毕竟并不是所有应用都对安全要求不高,并不是所有人都对安全漠不关心。能尽自己所能作多一点防护也没什么不好。多的就不说了,总之我还是比较鼓励人不断探索。
首先推荐一个很综合的讨论的链接:
http://segmentfault.com/q/1010000000095307
综合起来,密码的安全主要存在于三个方面:
- 数据库中存储密码的安全性
- 密码在网络中传输的安全性
- 密码在浏览器端输入的安全性(本文暂不讨论)
数据库中存储密码的安全性
有一篇文章可以算是概括了这一议题:
http://zhuoqiang.me/password-storage-and-python-example.html
简单的说,数据库中密码的安全性就是假如有黑客盗取了你的数据库的数据,你如何让黑客无法破解用户的密码信息。
最简单的方法,就是在数据库中使用明文来存储用户名密码(前一段时间CSDN的密码泄漏事件算是炸开了锅,更令人吃惊的是国内第一技术网站竟然使用这种低级的方法)。这种方法非常容易破解,只要得到了用户数据,任何人直接就能获得密码信息。
第二种方法是对密码进行加密(如使用MD5、SHA算法)之后存储在数据库中。这种方法对于较复杂的密码来说黑客束手无策,但对于较常见的密码黑客在得到了数据之后依然能有效破译。有一些好事者将用户常用的密码总结出来,再使用这些加密算法得出其加密后的值,称其为彩虹表。黑客只需要用彩虹表与加密后的密码比对,依然能得到用户的原始密码。
为了克服第二种方法的缺陷,使得常见密码也能得到保护,人们发明了第三种方法:使用salt来混淆加密后的值。大体方法是,每次写入用户的密码时(如注册或修改密码),随机生成一个salt值(可能是一个随机字串或者大整数等等),并将salt与密码混合(可以是各种混合方式而不仅限于将两个串连接在一起),再进行哈希。这样,即使黑客拥有了彩虹表,也不能立即猜测出哪些哈希值对应哪些常规的密码,因为即使用户输入了常规密码,混合了salt的哈希值已经与之前大相径庭,而在salt值对于不同用户各异的情况下(如果所有用户的salt值都一样且混淆方法已知那么黑客依然可以针对常见密码与salt混合生成一个具有针对性的彩虹表),也难以对所有用户生成一个彩虹表。但黑客依然可以针对某一个用户,使用暴力穷举的方式,来破译密码。如果用户的密码长度较短且全是数字的话,由于搜索空间较小且MD5、SHA等算法由于本身特性加密过程比较快,破译也并不难。
为了克服第三种方法的缺陷,第四种方法延长了加密算法的执行时间:或者使用“stretching”增加普通MD5等快速算法的迭代次数,或者使用bcrypt这样的执行时间可配置的加密算法,来迫使黑客在暴力破译的时候需要更长的时间。由于将加密算法控制在微秒级即可给黑客的破译带来灾难性打击而同时单个用户登录时验证的耗时又不算太长,这种方法可以说有效的解决了黑客破译密码的危险。
salt与彩虹表的内容有一篇文章讲得比较详细:
http://www.libuchao.com/2013/07/05/password-salt
关于bcrypt算法可见
http://codahale.com/how-to-safely-store-a-password/
其论文见
https://www.usenix.org/legacy/events/usenix99/provos.html
这篇文章讲了php中一个通用的考虑了以上所有问题的登录验证包phpass及其验证原理,还包括一些验证安全的通用议题,还讲到了如何使用输入过滤与prepared statement结合来防止SQL注入:
http://www.openwall.com/articles/PHP-Users-Passwords
不过笔者有一个小小的疑问,做了这么多都是为了防止黑客在得知了用户数据表之后如何防止其进一步得知用户的登录密码。但再进一步说,既然黑客都得到了用户数据表了,那么是不是也能得到其他信息呢?黑客想登陆到你的帐户无非也是为了获取甚至更改你的数据(至少很多时候这样),那么数据库本身的安全是不是比用户密码存储方式的设计更重要呢。看来安全并不是一个三言两语能讲完的议题,由于本文只关注密码在存储与传输过程中的安全,所以这些内容就只能暂时忽略了。
密码在网络中传输的安全性
一篇概括性讲解:
http://zhuoqiang.me/password-transport.html
传输过程中的安全性可能比较数据库存储的安全性更重要,因为网络中的数据包极有可能被黑客截获。
最不可取的方法就是使用明文传输密码,黑客可以轻松截获http传输的数据并获知密码(且不谈黑客如何从海量数据中获取和分析与登录密码相关的数据)。
进行加密传输后依然能被轻松攻击,黑客截获后,即使不分析出密文所对应的明文,只要构造相同的http请求使用所谓的“回放攻击”,就能轻松登录用户的账户。
解决这种传输中安全问题的终极方案是使用https协议加密传输。只要在客户端输入时密码不被窃取,且ssl协议的私钥安全,这种协议就是安全的。
值得一提的是,为了避免回放攻击,可以使用nonce来解决(参见http://doc.okbase.net/parry/archive/5619.html)。这种方式虽然应用在了http自带的认证中,普通的用户登录认证也可以仿效。但是这种方法貌似还是不如https来得安全。