加盐hash保存密码的正确方式

2015/10/25

Hash算法是一种单向的函数。它可以把任意数量的数据转换成固定长度的‘指纹’,这个过程是不可逆的。而且只要输入发生改变,哪怕只有一个bit,输出的hash值也会有很大不同。

加盐

查表和彩虹表的方式之所以有效是因为每一个密码都是通过同样的方式来进行hash的。如果两个用户是用了同样的密码,那么他们的密码hash也一定相同。我们可以通过让每一个hash随机化,同一个密码hash两次,得到的不同的hash来避免这种攻击。

具体的操作就是给密码加一个随机的前缀或者后缀,然后再进行hash。这个随机的后缀或者前缀称为‘盐’。所以盐一般都是跟hash一起保存在数据库里,或者成为hash字符串的一部分。

常见的使用盐的错误实现是:一个盐在多个hash中是用或者是用的盐很短。

java.security.SecureRandom

每一个用户,每一个密码都要使用不同的盐。用户每次创建账户或者修改密码都要使用一个新的随机盐。永远不要重复使用盐。盐的长度要足够,一个经验就是盐至少要跟hash函数输出的长度一致。盐应该跟hash一起存储在用户信息表里。

存储一个密码:

1. 是用CSPRNG生成一个长的随机盐
2. 将密码和盐拼接在一起,使用标准的加密hash函数比如SHA256进行hash
3. 将盐和hash记录在用户数据库中

验证一个密码:

1. 从数据库中取出用户的盐和hash
2. 将用户输入的密码和盐按相同方式拼接在一起,使用相同的hash函数进行hash
3. 比较计算出的hash跟存储的hash是否相同。如果相同则密码正确,反之则密码错误。

当用户忘记密码的时候,我应该怎样让他们重置

大多数网站是用绑定的email来进行密码找回。通过生成一个随机的、只使用一次的token,这个token必须跟账户绑定,然后把密码重置的链接发送到用户邮箱中。当用户点击密码重置链接的时候,提示他们输入新的密码。需要注意token一定要绑定到用户以免攻击者使用发送给自己的token来修改别人的密码。

token一定要设置成15分钟后或者使用一次后作废。当用户登录或者请求了一个新的token的时候,之前发送的token都作废。

Reference

http://drops.wooyun.org/papers/1066