加盐在解决什么
加盐是为每个口令生成一段唯一的随机值,与口令一起参与散列,并把盐随散列结果一起存储。它的作用是让相同口令得到不同散列,从而抵御彩虹表以及用户之间的批量比对。盐不是密钥,不需要保密,但必须唯一且足够长;它的价值在于破坏「一次计算、批量命中」的攻击模式。
基本信息
| 长度 | 建议至少 16 字节随机 |
|---|---|
| 存储 | 与散列一起保存,常直接嵌入散列字符串 |
| 作用 | 抵御彩虹表、隐藏相同口令 |
| 唯一性 | 每个口令一份,改密时必须更换 |
| 与 pepper 的区别 | pepper 是额外秘密,与散列分开保管 |
| 常见错误 | 固定盐、全局共享盐、短盐 |
典型使用场景
- 口令入库:注册与改密时生成新盐,与散列一起写入
- 迁移存量数据:为老散列补盐重算,用户下次登录时完成升级
- 密钥派生:为 KDF 提供每份数据独立的盐,避免相同输入产出相同密钥
- 审计检查:确认数据库里没有重复盐,也没有短于 16 字节的盐
- 多应用隔离:不同系统使用不同的额外上下文,避免散列口径互相套用
生成与使用
生成与使用
import os, hashlib, base64
salt = os.urandom(16) # 每个口令独立生成
dk = hashlib.pbkdf2_hmac('sha256', b'password', salt, 600000)
print(base64.b64encode(salt).decode(), base64.b64encode(dk).decode())
# 校验:从存储中取出盐与参数,用同样的流程重算并做常量时间比较
# 改密时必须重新生成盐,不要沿用旧值概念对照
| 概念 | 是否需要保密 | 作用 |
|---|---|---|
| 盐 | 不需要,公开存储 | 让相同口令产生不同散列 |
| pepper | 需要,与散列分开保管 | 增加全局秘密,降低拖库可利用性 |
| 迭代次数 | 不需要 | 抬高单次计算成本,需随硬件调整 |
| 算法标识 | 不需要 | 记录算法与参数,便于后续升级 |
固定盐、全局共享盐或短盐(如 4 字节)会让彩虹表重新有效;每次改密都要生成新盐。
盐要与迭代次数一起存:只保留散列与盐、不记录算法与参数,将来调整强度时老数据就无法校验。
盐只提高攻击成本而不消灭可能:攻击者仍可针对单个目标穷举,所以必须叠加慢散列与足够强度的口令策略。