密码存储与传输的那些事儿(一)密码存储概述

     最近看到一个项目,用明文存储的密码,让人不忍直视,所以就想聊聊密码的存储以及登陆过程中密码传输的那些事儿。

     首先就来看看密码存储,密码不能明文存储相信这个都不会有异议,那应该如何存储呢?    


方案1: 加盐哈希

       相信这个方案也是大多数公司目前使用的方案,至于哈希算法的话,普遍都采用MD5、SHA1、SHA256等算法。加盐的方式也各不相同,比如在密码尾部、在密码中间、在密码头部增加随机盐,有的甚至会使用多次HASH。

       不仅如此,甚至可以每次用户设置密码的时候,都为每一个用户生成随机盐,并将随机盐与用户信息一并存储(有时为了混淆,最好将随机盐与密码分表存储,也不要起SALT之类的列名)如此相信即使攻破的数据库,想要破解用户密码也是相当困难的。

       后续如果收到用户登陆请求,取出用户随机盐并通过相同的算法进行计算,将结果和数据库中密码相对比就可以判断用户密码是否输入正确。

        由于摘要算法的不可逆性,其实一定程度上就限制了登陆过程中用户密码的传输方式。例如:假设服务器的密码转换算法为MD5(SALT + PASSWORD),那么登陆时,有几种选择:

  • 传明文密码,在服务器完成密码转换后与数据库中密码进行比对;这种方式在非HTTPS情况下还是相当危险的,如果你是HTTPS的话,也可以用这种方式,但也不是太优雅;
  • 将用户随机盐发送给客户端,客户端再使用SHA256(SALT+PASSWORD)计算之后将结果传送给服务器,服务器直接与数据库中的匹配。这种方式虽然不再明文,但是存在两个问题:如果客户端是浏览器,你就很容易暴露你服务器内部的密码转换算法;如果你的随机盐是每个用户1个随机生成的,那么必然会存在多次交互,首先要根据用户登录名去查出其随机盐返回给客户端,之后在利用随机盐对密码进行转换,而且这种用户随机盐传给客户端也是不合适的。
  • 另外,你还可以使用非对称加密的方式,将公钥传递给客户端将明文密码加密再进行传输。

       针对上面的问题,其实个人建议还是使用复杂些的HASH算法,例如MD5(SALT + MD5(C-SALT + PASSWORD)),C-SALT可理解为服务器固定的盐,此处仅仅是为了使破解者的字典更难构造一些。如此客户端只需要将MD5(C-SALT+PASSWORD)的值发送到服务器,服务器再将随机盐取出,进行MD5即可,如此上面的方案1和2的缺陷即可避免。

       当然目前MD5和SHA1算法已经不是太安全,GOOGLE的Guava的库中都明确将md5方法标注为DEPRECATED,为此建议可使用SHA256来替代。       


方案2:加密技术

        这种方法其实个人并不推荐,但也有迫不得已的时候(例如我之前博客中提到的DIGEST认证面临的密码存储问题),我们在某些情况下需要利用用户原始密码进行一些认证。此时,就可以使用加密来存储密码,但是和哈希不同,加密都是可逆的,密码的存储就会成为大问题。

        其实也没有什么好的方式,可以和前面说的用户随机盐一样,每个用户每次修改密码都生成一个随机的密钥,然后存储为数据库中用户关联的某个字段,然后在进行密码加密。当然,这也是不得已而为之。


方案3:密码哈希

       是不是在疑问“密码哈希”和前面所说的MD5、SHA1、SHA256“哈希”有什么不一样?其实准确的来说,MD5、SHA1、SHA256叫做消息摘要更为合适一些。而密码HASH目前的常见方法有PBKDF2、Bcrypt(blowfish)、Scrrpt。

       这些密码哈希算法有一些特点:

  • 计算较为耗时:是不是觉得这应该是缺点而不应该是特点?这点恰恰就是他的优点,正因为耗时字典攻击的方式就在某种程度上被遏制了。而且其实用户对于登陆的时间并不是十分敏感,多等个几百毫秒,对计算机几来说是很长的时间,对用户登录来说,并不会影响用户体验。而MD5这种算法相对来说比较快,这也就是为啥叫他消息摘要算法的原因,他最原始的作用就是快速提取出数据的指纹。
  • 相同值HASH出的结果不一致,你需要特殊的比对方式,你也不必再多次一举的加盐。

    密码哈希算法个人认为是目前是这几个方案中最佳方案,很多开源项目对于密码存储都使用了密码哈希的方式(例如Openfire),为此,后续将结合具体协议和程序来分析一下各密码哈希算法的原理与优势。

你可能感兴趣的:(安全)