是什么
哈希函数(Hash Function):把任意长度的输入,映射成固定长度的输出(哈希值/摘要)。
密码学哈希的性质
一个安全的密码学哈希函数应满足:
一、确定性:相同输入永远得到相同输出。
二、单向性(抗原像性):从哈希值无法(在计算上)反推出原始输入。
三、抗碰撞性:很难找到两个不同的输入,使它们的哈希值相同。
四、雪崩效应:输入的微小改变(哪怕一个比特),会导致输出发生巨大改变(约一半的比特翻转)。
五、计算高效:正向计算很快。
常见算法
| 算法 | 输出长度 | 状态 |
|---|---|---|
| MD5 | 128 bit | 已不安全,不应使用 |
| SHA-1 | 160 bit | 已不安全,不应使用 |
| SHA-256 | 256 bit | 安全,广泛使用 |
| SHA-3 | 可变 | 安全,较新的标准 |
| BLAKE2/3 | 可变 | 安全,速度快 |
重要:MD5 和 SHA-1 都已被证实可以构造碰撞(2004 年 MD5 被破解,2017 年 Google 公布了 SHA-1 的实际碰撞攻击 "SHAttered")。
它们不应再用于任何安全场景。
主要用途
一、密码存储
绝不能明文存储密码!
正确做法:存储密码的哈希值。用户登录时,计算输入密码的哈希,与数据库中存储的哈希比对。
但仍不够——因为攻击者可以用彩虹表(预先计算好的常见密码哈希表)反查。
必须加盐(Salt):
- 为每个用户生成一个随机盐值;
- 把盐与密码拼接后哈希;
- 存储盐和哈希值。
这防止了彩虹表攻击(因为每个用户的盐不同,攻击者必须为每个用户单独计算)。
更进一步:用慢哈希函数(专门设计的、计算昂贵的算法)。
- bcrypt、scrypt、Argon2 是推荐的密码哈希算法;
- 它们被设计成故意慢且消耗内存,从而大幅提高暴力破解的成本。
Argon2 是 2015 年密码哈希竞赛的获胜者,是目前推荐的选择。
注意:普通的 SHA-256 计算太快,不适合直接用于密码存储——GPU 每秒可以计算数十亿次。
二、数据完整性校验
下载文件时提供的校验值(checksum),用于验证文件在传输中没有被损坏或篡改。
不过,单纯的哈希不能防止恶意篡改——因为攻击者可以同时修改文件和哈希值。防篡改需要数字签名或 HMAC。
三、数字签名
对消息的哈希值签名,而非对整条消息签名——因为哈希值长度固定且短,效率高。
四、去重与数据结构
- 哈希表:用哈希函数快速定位数据(前文提到过);
- 去重:比较哈希值而非比较完整数据;
- 布隆过滤器:用多个哈希函数实现高效的"可能存在"判断(内存占用极小,可能有假阳性但无假阴性)。
五、区块链与工作量证明
区块链用哈希函数构建链式结构(每个区块包含前一个区块的哈希),并用哈希计算作为工作量证明的基础。
六、内容寻址
内容寻址存储(如 IPFS、Git):用内容的哈希作为标识符。
Git 就是这样——每个对象(文件、目录、提交)都由其内容的哈希(SHA-1)标识。这意味着相同内容只会存储一次,且任何修改都会被检测到。
注意:Git 使用 SHA-1 不是为了防碰撞攻击(它主要用于完整性检测,而非安全签名),但 Git 社区也在向 SHA-256 迁移。
非密码学哈希
不是所有哈希都用于安全:
- CRC32:错误检测(如网络传输、压缩文件),不抗恶意篡改;
- MurmurHash、CityHash、xxHash:为速度优化,用于哈希表等非安全场景。
不要用非密码学哈希处理安全相关的数据。
说明:本文为密码学基础的知识整理。