【Security】用户验证及密码加密存储

Posted by 西维蜀黍 on 2018-02-23, Last Modified on 2021-09-21

验证思路的本质

验证思路的本质无非是对比当前用户输入的账号密码与正确的(且存储于数据库中的)账号密码是否一致,若一致则认为身份验证通过。

密码存储策略

store passwords as plain-text

考虑到安全性因素,原始密码当然不能明文直接存储于数据库。

Brute force Attacks

Brute force is the action of trying out all the possibilities iteratively following a generation rule. It’s like when we try to open a padlock by listing all the possibilities from 0000 to 9999 until the lock opens.

Hash(password)

经过散列处理(如MD5),并将散列后的字符串进行存储,是一种存储密码的方法。

因为,散列后的值一般是无法通过特定算法得到原始字段(对应于原始密码)。

Dictionary Attack(字典法攻击)

然而,通过一个很大的哈希映射表,并在表中搜索该MD5值,很有可能就在极短的时间内找到该散列值对应的原始字段。

这种攻击方法本质上是利用给特定的一个哈希函数一个特定的 input ,都一定会得到一个不会改变的 output。因此,攻击者可以提前构造好一个原始字符串到 hash 值的映射表。

最终,如果在这个映射表中找到了一个哈希值时(能不能找到取决于这个映射表有多大),就可以将 hash 值还原成原始密码了。

Rainbow Table Attack(彩虹表攻击)

Rainbow table

A rainbow table is a precomputed table for caching the output of cryptographic hash functions, usually for cracking password hashes. Tables are usually used in recovering a key derivation function (or credit card numbers, etc.) up to a certain length consisting of a limited set of characters.

It is a practical example of a space–time tradeoff, using less computer processing time and more storage than a brute-force attack which calculates a hash on every attempt, but more processing time and less storage than a simple key derivation function with one entry per hash.

Use of a key derivation that employs a salt makes this attack infeasible.

Hash with Salt

进而,我们采用加盐(salt) + 散列处理的方式。即,在进行散列处理之前,在原始字段的任意固定位置插入特定的字符串(salt值),其作用是让加盐后的散列结果和没有加盐的散列结果不相同,最终使得依赖彩虹表的破解方式以还原出原始密码的概率大大降低。

Hash Functions

There are a number of modern hashing algorithms that have been specifically designed for securely storing passwords. This means that they should be slow (unlike algorithms such as MD5 and SHA-1, which were designed to be fast), and how slow they are can be configured by changing the work factor.

Common mistakes

Salt re-use

注意,这里的salt值,应该是为每个密码都生成一个对应的salt值(并将这个salt值明文存储于数据库中)。 一个常见的错误,是所有用户的密码使用同样的一个salt值(硬编码(Hard Code),作为一个静态变量存储于执行的程序集中),这样称为盐值复用(Salt Reuse)

Short salt

If a salt is too short, an attacker may precompute a table of every possible salt appended to every likely password. Using a long salt ensures such a table would be prohibitively large.


从代码实现的角度来说,在每个用户注册时,用户都需要设置自己的账号和密码。

  1. 这时,我们为这个用户生成一个高长度的随机数作为salt值。而不同的用户注册时,都会分别进行一次随机数生成。因此每个用户都对应一个特定的salt值;
  2. 将用户设置的原始明文密码+salt值作为一个整体,并进行散列处理,这个结果暂时称为“加盐后的密码散列值”;
  3. 将用户账号名、”加盐后的密码散列值“和盐值存储于数据库中;
  4. 此后每次用户登录时,将“此时用户尝试进行登录的密码”+“数据库中读取的对应该用户的盐值” 看做一个整体,并进行散列处理计算,若这个结果与“数据库中存储的加盐后的密码散列值”相同,则验证通过。

这样我们就能保证,即使我们的数据库被入侵者获取并全表下载后(拖库),黑客也几乎不可能将用户的登录密码还原出来。

加盐后的密码破解

经过加盐后,并不意味着被黑客拖库后就不能还原出原始密码,只是破解的成本被大大提高。

假设加盐值有1000种可能,一张彩虹表100G,如要暴力破解以测试这1000种可能,则需要100GB * 1000 = 10TB 的空间。

更何况我们还可以将salt值设置为一个高长度的随机数,最终使得通过暴力破解或者原始密码的可能性变为微乎其微。

Reference