跳到正文
ZH

学习 · 指南

哈希是什么(以及什么时候不该用它)

讲清单向函数:校验和、密码存储,以及 SHA-256 为什么不是加密。

单向函数

哈希接收任意输入,一句话、一个文件、一个密码都行,然后产出固定长度的输出。SHA-256 总是返回 64 个十六进制字符。决定性的性质是它是单向的:给定哈希,你无法还原出输入。改动一个字符,整个哈希就变了,这让哈希极擅长检测变化,却极不适合当任何需要取回内容的存储格式。

三种常见用途

校验和(SHA-256、CRC32)。 下载一个文件,在本地算哈希,和发布方印出来的哈希比对。如果一致,说明文件完整送达。CRC32 更弱但更快,适合日常的损坏检查。

密码存储(不是用裸 SHA-256)。 密码需要慢且加盐的函数,比如 bcrypt、argon2 或 scrypt。SHA-256 这类快哈希能让攻击者在 GPU 上每秒试几十亿个猜测。这个场景里,选那个「看起来很现代」的算法恰恰是一个安全错误。

去重与索引(MD5、CRC32)。 识别重复内容或分片键用的是哈希的速度,不是安全性。MD5 的碰撞在这里无关紧要。

哈希为什么不是加密

加密可以用密钥还原,哈希则完全不可逆。两者被混淆是因为都会产出不透明的字符串,但把哈希当成加密数据,或者去「解密」一个哈希,在设计上就是不可能的。如果某个服务声称能解密哈希,它几乎肯定是在拿一堆输入去比对查找表,这也是唯一性高的加盐值如此重要的原因。

选择速查

  • 检查下载文件的完整性 → SHA-256(或者用 SHA-512 留更多余量)。
  • 对本地备份集做快速损坏扫描 → CRC32 或 SHA-1 换速度;这里碰撞无所谓。
  • 内容去重或构造查找键 → MD5 或 SHA-256;只有分布性重要。
  • 存储密码 → 不要自己造轮子。用 bcrypt、argon2id 或 scrypt,配每个用户独立的盐;每种语言都有现成的库。
  • 给消息签名 → 不能用裸哈希。用 HMAC(带密钥的哈希)或正规的签名方案。

「加盐」为什么改变一切

单靠哈希藏不住一个弱密码:攻击者拿常见密码字典逐个算哈希再比对结果,这就是不加盐的 MD5 密码泄露能被这么快破解的原因。每个用户一个随机盐就让每个哈希都变得独一无二,同一个密码存两次会得到两个不同的哈希,攻击者必须逐个单独破解。慢且吃内存的函数(argon2id、scrypt)再把每次猜测的成本抬高几个数量级。如果你是在动手写密码系统之前读到这段,那么这两条性质——每个用户独立盐,加上慢算法——就是最低要求。

亲手试

打开 ToolsKit 的 哈希生成器,看 password 和 Password 产出完全不同的 SHA-256 值,然后把同一个字符串算两遍,确认它是确定性的。这种确定性加上单向性,就是它的全部超能力。