技术解析2026年6月27日

别用 MD5 存密码:哈希、加盐和口令哈希到底差在哪

MD5/SHA-256 是哈希算法,但直接拿来存密码是错的。本文讲清哈希、加盐、口令哈希(bcrypt/Argon2)三者的区别,以及为什么校验文件用 SHA-256、存密码却要慢哈希。

"用 MD5 加密密码"这句话里有两个错:MD5 不是加密、而且它不该用来存密码。哈希、加盐、口令哈希常被混为一谈,但它们解决的是完全不同的问题。理清这三层,才能判断什么场景该用哪种,以及为什么校验文件和存密码要用截然相反的算法。

哈希单向、加盐、慢口令哈希的区别

哈希不是加密,先分清这点

哈希(hash)是单向的:把任意输入压成固定长度的指纹,无法从指纹反推原文。加密(encryption)是双向的:用密钥能加密也能解密。所以"MD5 加密"是个常见误称——MD5 是哈希,没有"解密"一说。

哈希的两个核心性质决定了它的用途:

  • 确定性:同样的输入永远得到同样的输出。这让它能当"指纹"用于校验。
  • 抗碰撞:很难找到两个不同输入产生相同哈希。这让指纹难以伪造。

通用哈希(MD5、SHA-1、SHA-256、SHA-512)还有第三个特点:为速度优化,算得飞快。这个"快"在文件校验里是优点,在存密码里却是致命伤——下面就分场景看。

为什么不能用通用哈希存密码?

因为通用哈希太快了。结论先说:MD5/SHA-256 这类哈希每秒能在普通硬件上算几十亿次,攻击者拿到哈希后可以高速暴力枚举或查表反推,尤其对常见弱密码几乎瞬破。

具体有两种攻击:

  • 彩虹表(rainbow table):预先把海量常见密码的哈希算好存成表,拿到一个哈希直接查表命中。因为哈希是确定性的,123456 的 SHA-256 永远是同一个值,一查就中。
  • 暴力 / 字典枚举:现代 GPU 每秒能算数十亿次 SHA-256,按字典和规则猛试,弱密码扛不住几秒。

哈希快,本意是好事,但用来存密码就等于给攻击者送了高速试错的靶子。所以存密码需要反其道而行——故意让哈希变慢。

加盐:让相同密码不再相同

加盐(salt)解决的是"相同密码产生相同哈希"这个结构性弱点。做法是给每个用户生成一段随机字符串(盐),把"盐 + 密码"一起哈希,并把盐和结果一起存。

它带来两个直接效果:

  • 彩虹表失效:预先算好的表是针对"裸密码"的,加了随机盐后哈希结果完全不同,表里查不到。
  • 无法一次破多个账户:每个用户盐不同,即使两人用同一密码,哈希也不同,攻击者只能逐个账户单独破,成本陡增。

但要注意:加盐只防"批量/查表"攻击,不解决"哈希太快"。攻击者拿到某个用户的盐和哈希后,仍可以针对这一个账户高速枚举。要堵这个口子,得换算法本身。

口令哈希:故意做得又慢又吃内存

针对"哈希太快",密码学界设计了专门的口令哈希(password hashing)算法——bcrypt、scrypt、Argon2。它们的核心思路是故意昂贵

算法 关键设计 防御重点
bcrypt 可调"工作因子",迭代次数指数增长 拖慢 CPU 暴力
scrypt 高内存占用 拖垮 GPU/ASIC 并行
Argon2 时间+内存+并行三参数可调 综合抗 GPU/ASIC,现代首选

它们内置加盐,并允许你调一个"代价参数":让单次哈希耗时几十到几百毫秒。对正常登录,几百毫秒无感;对攻击者,每秒只能试几千次而非几十亿次,暴力破解的成本被抬高几个数量级。而且随着硬件变快,只要调高代价参数就能持续保持难度。

那 SHA-256 到底什么时候用?

通用哈希没被淘汰,只是用对场景:它适合"要快、要稳定指纹"的任务,而不是存口令。

  • 文件完整性校验:下载完算一遍 SHA-256,和官方公布的值比对,确认没被篡改或传输出错。这里要的就是快和确定性。
  • 内容寻址 / 去重:用哈希当数据的唯一标识(如 Git 的对象、CDN 缓存键)。
  • 数字签名的预处理:先哈希再签名,缩短待签数据。

这些场景里,对一段文本或文件算一遍 MD5、SHA-1、SHA-256、SHA-512 得到指纹做比对就够了——因为目标是生成稳定指纹,而非抵御针对口令的暴力枚举。这也正是通用哈希"快"的价值所在。

一张表收束:哪种需求用哪种

把三层和场景对齐,选择其实很清晰:

需求 该用什么 不该用什么
校验文件没被篡改 SHA-256 ——
数据去重/唯一标识 SHA-256 等 MD5(碰撞已被攻破)
存用户密码 Argon2 / bcrypt / scrypt + 盐 MD5 / SHA-256 裸哈希
防彩虹表/批量破解 每用户随机盐 全局固定盐或不加盐

判断标准很简单:要快、要指纹 → 通用哈希;要扛暴力枚举 → 口令哈希;要防批量/查表 → 必须每用户加随机盐。 用错方向(拿快哈希存密码,或拿慢哈希校验大文件)才是真正的问题。

小结

哈希不是加密;通用哈希(MD5/SHA-256)为速度而生,适合文件校验和去重,但正因为快,直接拿来存密码会被彩虹表和 GPU 暴力枚举轻易反推。加盐解决"相同密码同哈希"和批量破解,但不改变"太快";真正存口令要用 bcrypt/scrypt/Argon2 这类故意又慢又吃内存的口令哈希,并可随硬件升级调高代价。一句话:校验文件用 SHA-256,存密码用 Argon2 加盐,别把两者用反。

本文用到的工具

常见问题

不应该。MD5/SHA-256 是为速度设计的通用哈希,攻击者每秒能算几十亿次,配合彩虹表或暴力枚举可以快速反推常见密码。存密码要用专门的口令哈希算法(bcrypt、scrypt、Argon2),它们故意做得慢且吃内存,让暴力破解代价高到不可行。