本文讨论存储密码的加盐机制。

Linux用户和密码保存提到过:现今Linux存储用户密码是加盐(salting)的,即类似于:

这是现今密码存储的state of art,相信不仅仅是Linux,包括Mac OS和Windows存储用户密码的方式也应该是类似的。

那么为什么存储用户密码需要加盐呢?

其中一个最重要的原因,是因为加盐后attacker破解密码的计算复杂度提高是数据表中salt的数量。

1.不加盐

为了理解这个问题,先来看看不加盐的情况。

user id user hash(pwd)
1 alice hash(pwd_alice)
2 bob hash(pwd_bob)
3 mary hash(pwd_mary)

假设上表是用户密码存储在数据表中的真实情况,在不加盐的情况下,用户密码直接存储为:

假设attacker使用dictionary attack的方式来猜测用户的密码。假设他已经有一张precomputed talbe,里面包含了所有常用的密码以及对应的哈希值,即键值对

那么他只需要检查precomputed table里的每一个哈希值是否出现在数据表中,即是否有:

那这样的计算复杂度是多少呢?

假设precomputed table里面包含了个这样的键值对,并且查询每一个键值对的哈希值是否在数据表中的计算复杂度是,那么有

这是不加盐的情况。

2.加盐(salting)

现在看看加盐后有什么不同。

user id user salt hash(pwd)
1 alice 123 hash(pwd_alice/123)
2 bob 456 hash(pwd_bob/456)
3 mary 789 hash(pwd_mary/789)

加盐后,数据表中存储的用户密码形式成为

注意salt的保存形式:

  1. 每一个用户的salt应该不同
  2. salt是以明文的形式存储在数据表中的,所以并不能增强密码的安全性

那么现在attacker同样使用字典攻击的计算复杂度是多少呢?

最关键的是:现在查询每一个键值对的哈希值是否在数据表中的计算复杂度已经由提高到,其中是数据表中salt的数量。

为什么呢?

因为attacker并不知道哪一个salt是配对哪一个password的,他唯一的方法就是把每一个salt对应他猜测的每一个password都试一次,即

所以,同样假设precomputed table里面包含了个的键值对,attacker字典攻击的复杂度从没有加盐之前的提高到了加盐之后的

这是我认为使用加盐存储密码的一个主要原因。另外使用加盐的一些好处:

  • 用户设置的密码普遍偏短,加盐相当于延长了用户密码
  • 假设不同用户使用了同一个密码,加不同的盐会使哈希值不同

本文参考了1

  1. Kaufman, Charlie, et al. Network Security: Private Communication in a Public World, Prentice Hall PTR, 2002.