巨大的输出空间
大多数现代哈希算法(如 SHA-256)产生 256 位的输出。这意味着总共有 2^256 种可能的哈希值——这是一个天文数字(约 10^77)。现实世界中的数据量与之相比极其微小,所以随机碰撞的概率极低。
雪崩效应
好的哈希算法具有“雪崩效应”:输入中哪怕只有 1 比特(一个二进制位)的变化,输出的哈希值中大约会有一半的比特位发生翻转。这导致哈希值在输出空间中是均匀分布的。没有“聚集”现象,也就不会让某些哈希值更容易被命中。
雪崩效应 (Avalanche Effect) :当输入发生微小的改变(例如,只翻转一个比特位,即把 0 变成 1 或 1 变成 0),输出的哈希值中,平均大约有一半(50%)的比特位会发生翻转
为什么是 50%? 如果是 0% 或 100%,说明函数是线性的或常数,特别容易推出来。50% 意味着改变是随机的、均匀的,且无法从输出的变化反推输入的变化
输入1: Hello World
哈希1: a591a6d40bf420404a011733cfb7b190d62c65bf0bcda32b57b277d9ad9f146e
输入2: hello World
哈希2: f7e49140acb3b76501d2e9395a1a89faa855dbfd1b529caf7a9e1c87f7fec974
不同的比特位数: 129 / 256
变化比例: 50.39% # 非常接近理想的 50%
确定性 + 伪随机性
相同的输入永远得到相同的输出,但不同的输入产生的输出看起来像独立、均匀的随机数。这使得我们可以用生日悖论来估算碰撞概率:
生日悖论:在一个仅有23人的群体中,至少有两个人生日相同的概率就超过了50% 计算“至少有两个人生日相同”的概率比较复杂,一个更简单的方法是计算它的对立事件——“所有人的生日都不同”的概率,然后用1减去这个值。
我们假设一年有365天,且每天出生的概率均等。
- 第一个人的生日:可以是365天中的任意一天,概率为
365/365。- 第二个人的生日:为了不与第一个人重复,他/她有
364天可选,概率为364/365。- 第三个人的生日:为了不与前两个人重复,他/她有
363天可选,概率为363/365。- 以此类推,直到第23个人,他/她有
343天可选,概率为343/365。那么,23个人生日都不同的概率是:
P(都不同) = (365/365) × (364/365) × (363/365) × ... × (343/365) ≈ 0.493因此,至少有两个人生日相同的概率是:
P(至少两人相同) = 1 - P(都不同) ≈ 1 - 0.493 = 0.507
- 对于 256 位哈希,要获得 50% 的碰撞概率,需要大约
2^128次哈希计算。 2^128是一个远超当前全球总计算能力的数量级。换句话说,在你的有生之年,即使动用全世界的计算机,碰撞发生的概率也无限趋近于零。
抗碰撞设计(针对加密哈希)
对于加密哈希(如 SHA-256, SHA-3),算法经过专门设计以抵抗两类攻击:
- 弱抗碰撞:给定一个哈希值
h(x),很难找到另一个y使得h(y) = h(x)。 - 强抗碰撞:很难找到任意两个不同的
x和y,使得h(x) = h(y)。
弱抗碰撞
这个很难找到,并不是指的数学上找不到,而是在你我以及全人类目前拥有的计算能力下,即使耗尽宇宙寿命也算不出来。
假设一个哈希函数 H,输入是任意长度的二进制数据,输出是 4 位(16 种可能)
给定一个哈希值 1010。若“一定能找到一个 y 使得 H(y)=1010
- 数学上: 正确。因为输入无限(
0, 1, 00, 01, ...),而输出只有 16 种,根据鸽巢原理,每种输出都有无限多个原像。所以解一定存在 - 但实际中:如果
H是密码学哈希,只能暴力尝试。平均需要尝试 2^3 = 8 次(对于 4 位),这很容易。但对于 SHA-256(256 位),你需要平均尝试 2^255 次,这远超宇宙原子总数
强抗碰撞
若定能找到两个不同的 x 和 y 使得 H(x)=H(y)
- 数学上: 正确。因为输入空间(比如所有 5 位长的二进制串,共 32 种)大于输出空间(16 种),根据鸽巢原理,必然存在碰撞。
- 实际中:对于 4 位哈希,暴力枚举 32 个输入就一定能找到。但对于 SHA-256,你需要枚举大约 2^128 个输入才能以 50% 概率找到一对碰撞,这同样做不到
实际应用中的处理方式
-
安全场景(密码、数字签名) :依赖加密哈希的碰撞阻力。一旦发现算法有理论碰撞(如 MD5, SHA-1 已被攻破),就立即淘汰,升级到更强的算法(如 SHA-256, SHA-3)。
-
非安全场景(哈希表、散列表) :冲突一定会发生(因为输出空间很小,比如 32 位)。此时不是靠算法避免冲突,而是用冲突解决机制来处理:
- 链地址法:同一哈希值的元素存在一个链表里。
- 开放寻址法:冲突后找下一个空槽位。
- 等等等等....
MD5被攻破,恰恰不是因为它的“单向性”(不可逆)出了问题,而是它的“抗碰撞性”被找到了漏洞
简单来说,MD5依然“单向”,但你可以在不破坏单向性的情况下,人为制造出“两个不同输入,算出一个相同哈希”的情况。这就像两把不同的钥匙,恰好能打开同一把锁