用户密码的加密存储

早期的软件应用在数据库中存储用户密码时,使用的是明文存储,数据库数据泄漏就意味着用户密码的直接暴露。后来通过 MD5 等算法对密码明文进行加密后再存储到数据库中,因为 MD5 算法是不可逆的,即从数学逻辑上来讲无法通过一个加密后的密文反推出原始明文,所以这种方式还算是相对安全的。但是很多用户总是喜欢使用简单易记的密码,这在数据库被脱库,加密后的密文全部暴露的情况下,攻击者可以使用多种方式来破解密码。

密码破解

  • 字典和暴力破解

简单的暴力破解就是猜密码,对每一个可能的密码进行 Hash,然后与泄漏的密文进行比对,如果两者一样,则密码原文一致。好事者将常见的密码收集起来放到一个文件中,然后对文件中每一个词进行 Hash,再与密文比对,这种方式就是字典攻击。

字典攻击和暴力破解针对的是常见简短密码,这种方式的成功率取决于字典的大小以及字典的选择是否合适,密码长度越长,字符组合的可能性就越多,需要的存储空间就越大,效率也就更低。虽然这种方式的破解比较原始,却也没有办法阻止,唯一可行的方法就是让他们的破解尽可能的低效,使破解的成本增加,迫使攻击者放弃破解。

  • 查表破解

对于特定的 Hash 算法,如果需要大量破解的话,查表是一种非常高效的方式。与字典类似,提供部分常见密码和计算好的 Hash 值,存放到设计合理的数据库表中,利用数据库查询的优势提高效率。

  • 反向查表破解

攻击者会根据泄漏的数据库数据制作一个用户名和对应的 Hash 表。然后将常见的字典密码进行 hash 之后,跟这个表的 hash 进行对比,就可以知道用哪些用户使用了这个密码。这种攻击方式很有效果,因为通常情况下很多用户都会有使用相同的密码。

  • 彩虹表

彩虹表的根本原理就是组合了暴力法和查表法,并在这两者之中取得一个折中,用我们可以承受的时间和存储空间进行破解。

  • 碰撞破解

与暴力破解不同的是,碰撞破解是寻找一个与原文不同的内容,经过 Hash 后与原文的 Hash 值相同。04 年的时候,王小云教授提出了 MD5 快速碰撞的方法,大大减少了碰撞尝试的次数。

防护手段

加盐

单纯使用 hash 算法对密码进行加密,攻击者很容易根据常见密码制作字典进行暴力破解,一个好的方式就是加盐处理。简单来说就是使用 hash(密码 + 盐) 的方式进行加密,并且这个盐最好使用随机字符串生成,每个用户都有一个且都不一样。数据库可以简单设计成以下形式:

数据库设计

同时,加盐需要注意的以下几点:

  • 不要使用重复的盐

不管是将盐硬编码在程序中,还是使用一个随机生成的盐,在系统中所有的密码都使用同一个盐是不可取的。一旦攻击者获取到该盐,就可以用来制作字典或者使用彩虹表进行暴力破解。

  • 不要使用过短的盐

如果盐的位数太短的话,攻击者也可以预先制作针对所有可能的盐的查询表。过短的盐会使攻击者制作盐查询表的成本降低。

  • 不要使用用户名作为盐

同样的理由,用户名虽然不可预测,但是并不是完全随机的。攻击者同样可以针对常见的用户名作为盐制作查询表和使用彩虹表暴力破解。

  • 要使用安全可靠的伪随机数生成器

伪随机数生成器(Cryptographically Secure Pseudo-Random Number Generator,CSPRNG)提供一个高标准的随机数,是完全无法预测的。比如 Java 中的 java.security.SecureRandom

参考

彩虹表 RainbowCrack

从 hash 函数到王小云的 MD5 破解

如何安全的存储用户的密码