一句话:哈希加盐(Salted Hash)是给每个密码先拼接一段随机字符串(盐,salt),再交给哈希函数,最后把结果存进数据库的做法。
哈希函数(hash function)像绞肉机:一整块肉放进去,出来的是固定重量的肉馅,且永远还原不回去。同样的输入永远得到同样的输出——这是它的价值,也是裸哈希的麻烦:两个用户都设 123456,库里就是两条一模一样的字符串。攻击者拖库后什么都不用破,就能看出“谁和谁同密码”,再拿常见密码表批量对照。
打个比方:裸哈希像把全班同学的指纹各压成一张卡片,单看一张认不出是谁,但摊开一比就知道谁和谁同指纹。加盐是给每个人配一把独有的模具再压一次,同样的手指,压出来的卡片互不相同。
它具体防两件事:一是相同密码不再产生相同哈希;二是让彩虹表(rainbow table)失效——那是一本提前算好的“常见密码→哈希”大字典,加了盐,字典里每一条都得针对这个盐重算,成本乘以用户数。盐不必保密,与哈希一起存库里就行,但要随机、一人一份、足够长。
再说它防不住什么,这更要紧:
- 防不住弱密码。攻击者盯上某个账号,拿 123456 加他的盐现算一次,照样对得上。盐把“破一次打穿全库”降级成“破一次只打一个人”,但没让这个人变强壮。
- 防不住拖库本身,防不住撞库(credential stuffing,拿别处泄露的账号密码来试你的站),也防不住钓鱼和内鬼。
- 不解决“单次尝试太便宜”。所以要再叠一个慢哈希,也就是密钥派生函数(KDF,key derivation function),如 bcrypt、scrypt、Argon2 这类专为存密码设计的算法,把每次计算的成本抬高。盐加慢哈希,才是现代存密码的及格线。
| 做法 | 可还原原文 | 同密码同结果 | 主要防什么 |
|---|---|---|---|
| 裸哈希 | 否 | 是 | 只防被人直接读到密码 |
| 加盐哈希 | 否 | 否 | 预计算、批量比对 |
| 加盐 + 慢哈希 | 否 | 否 | 再加上暴力尝试太贵 |
| 加密(encryption) | 是 | 视实现 | 需要取回原文时用,不能拿来存密码 |
顺带分清:加密可逆、哈希不可逆;胡椒(pepper)是全局共享、放在代码或密钥服务里的秘密,和一人一份、明文存放的盐不是一回事。
对做 AI 的人同理:API key、口令这类东西,要“能验证”就用加盐哈希或 HMAC,要“能取回”才用加密并托管密钥;别让明文密码流进日志和训练数据。对普通人:看到“我们用了加盐哈希”是及格线不是满分,更该追问有没有上慢哈希和多因素认证——以及,任何网站都不该能告诉你原密码,只能让你重置。具体算法与参数以官方文档为准。
