为什么需要加盐
在计算机领域经常能够听到 "加盐" 这个词, 本文将介绍什么是 "加盐", 为什么需要 "加盐"?
场景
"加盐" 操作经常出现在密码存储场景. 作为一名负责任的后端开发, 任何时候都不应该存储用户的明文密码, 因为一旦系统被"脱裤", 将会导致用户密码暴露的风险
"脱裤"谐音拖库, 指数据库被黑客攻破后所有的数据被拖走
要想得到密文密码, 有两种常见的方式, 一种是加密, 在这个场景不太适合, 因为加密需要的密钥存储是个问题, 如果密钥直接存放在数据库里那黑客完全可以通过密钥恢复明文, 加密也就失去了意义. 另一种方式是哈希
哈希
什么是哈希? 首先要明确哈希不是加密, 哈希不需要密钥. 哈希是一个函数, 能够把无限的输入映射为固定长度的输出, 大概公式表示如下
具体概念我们不深入, 只需要知道下面主要特点
- 存在哈希碰撞, 简单说就是对于多个不同的输入 可能得到相同的输出
- 算法确定, 那么固定的输入 只会得到固定的输出
- 哈希函数理论上不可逆, 我们可以轻易通过输入 得到输出 , 但是通过输出 反推输入 将会非常困难甚至不可能
- 优秀的哈希函数通常把输出设计得很随机很离散, 不会有输入 "123" 输出 "A", 输入 "124" 输出 "B" 这种非常好猜的规律
通过哈希函数, 我们可以非常简单的将明文变成密文, 但是拿到密文则无法得到明文
彩虹表攻击
我们已经了解了哈希函数的单向性, 不考虑算法缺陷的情况下, 攻击者真的无法通过密文得到明文吗?
答案是否定的. 我们对于哈希函数无法反推的前提是输入随机, 长度不限, 而实际中系统肯定要限制 用户的密码长度, 这使输入空间局限在一个相对小的范围. 另一个影响输入的巨大因素是用户往往会选择简单好记的密码, 比如有多少人的密码是 "12345678"?
那么我们是否可以建立一张表, 对于所有简单的密码都预先计算哈希值, 通过对比密文与哈希值, 如果一致则说明明文应该也是相同的, 这就是所谓的彩虹表攻击
加盐
终于进入我们今天的主题, 我们把哈希函数的公式改一改
"加盐" 操作非常简单, 就是把用户输入的明文 与我们的盐值 进行字符串拼接, 将拼接后的字符串再进行哈希操作, 得到想要的密文
如果输入 不是字符串需要序列化成字符串
只需要小小一步 "加盐" 操作, 我们就能把彩虹表攻击扼杀在第一步, 攻击者连输入都构造不出来
是否需要不同的盐值
如果我们为所有的密文都选择相同的明文, 是否容易被攻破?
我们知道众多的用户通常会选择相似的密码, 使用一个盐值也就得到相同的密文, 出现次数的最多密文对应出现次数最多的明文, 利用统计学规律可以猜出明文, 结合已知的明文和密文, 我们可以对盐值建立彩虹表, 从而暴力破解盐值, 盐值一破解明文也无处可藏
为保证安全性, 应该选择足够大足够随机的盐值, 这能极大提高攻击者的难度
为每个密码设置一个不同的盐值, 攻击者攻击时需要为每个密码建立彩虹表, 这个难度使破解变得几乎不可能
一密一盐 使安全性得到了保证, 不过盐值的选取和存储成为了新的问题, 有些比较优秀的算法比如 bcrypt 就为了解决这个问题, 本文就不扩展讲了, 以后有机会聊聊