哈希与加密算法速查表
哈希与加密算法速查:MD5/SHA-1/SHA-256/SHA-3/HMAC 等哈希算法,bcrypt/scrypt/Argon2 等密码哈希,AES/RSA/ECC 加密算法的输出长度、安全性与最佳用途。
原理解析
哈希(散列)把任意长度输入映射为固定长度的摘要,且是单向的:从原文容易算摘要,从摘要几乎无法反推原文。理想哈希具备雪崩效应(输入改 1 位,输出彻底改变)与强抗碰撞性。
常见家族:MD5/SHA-1 已因碰撞攻击被淘汰于安全场景;SHA-256 属 SHA-2 仍安全;SHA-3 为另一设计。用途涵盖完整性校验、数字签名、口令存储(须加盐防彩虹表)。安全场景切勿再用 MD5/SHA-1。
什么时候该查这张表
这张表在你要为某处数据选择哈希算法时有用。它列出 MD5、SHA-1、SHA-2 系列、SHA-3 系列、CRC、xxHash 等算法的类型、输出长度、安全性与最佳用途。选哈希前先分清是「要防篡改」还是「仅做分片/去重」。
不该查的情况:你在设计密码存储方案,或需要抗碰撞的签名。哈希算法的碰撞概率与「防篡改」是不同维度的问题;密码存储要用专门的 KDF(Argon2、bcrypt、scrypt),而不是任何摘要算法。
判断标准:先问用途。数据完整性校验(文件是否损坏、下载是否被改)⇒ CRC32 或 xxHash 快且够用;防伪与签名(攻击者会主动构造碰撞)⇒ SHA-256 以上;已不该用于安全目的的 MD5/SHA-1 只在兼容老系统时保留。
特别注意长度扩展攻击:SHA-256 本身不抵抗长度扩展攻击(SHA-3、Blake2 抵抗)。如果你的协议是「H(secret || message)」这种构造,攻击者能在不知道 secret 的情况下扩展出 H(secret || message || padding || extra)。需要 MAC 就直接用 HMAC,不要自己拼。
边界与常见陷阱
MD5 与 SHA-1 已不安全,碰撞是可构造的
MD5 与 SHA-1 都已出现实用可行的碰撞构造(MD5 几秒内可造出两个不同文件同哈希;SHA-1 的 SHAttered 攻击 2017 年公开且成本已降至约 4.5 万美元并持续下降)。后果是:文件签名、证书完整性、去重键校验全部可被绕过。碰撞不一定能直接用于第二原像攻击,但签名场景已经足够。新代码一律用 SHA-256 或 SHA-3/BLAKE2。
自拼 H(secret || data) 不等于 MAC
「hash(secret + message)」 存在长度扩展弱点:攻击者即使不知道 secret,也能给出 hash(secret + msg + padding + extra) 的合法值,从而篡改「追加」部分。SHA-256 抗碰撞但不抗长度扩展(SHA-3、BLAKE2 抗)。正确做法是用 HMAC(「HMAC-SHA256」),它从设计上处理了这个问题;Node 里用 crypto.createHmac。
加密散列不代表能存密码
通用哈希(哪怕 SHA-256)设计目标是快,这正是密码存储最不需要的性质 —— 攻击者用 GPU 每秒能算几十亿次。密码必须用故意慢且自带盐与成本参数的 KDF:Argon2id(首选)、bcrypt、scrypt、PBKDF2。这三者内部再加盐并可调迭代次数,让暴力破解成本高到不可行。迁移旧哈希时也要用「登录时重新计算并升级」的渐进方案。
与相邻内容的分工
要生成或校验哈希值,用站内哈希工具类;本页是算法选型对照,不提供计算能力。
要处理 UUID 与各种标识符的生成与选择,用站内 UUID 与随机工具;哈希不能替代唯一标识符。
涉及数字签名与证书链的完整设计,属密码学体系范畴,本表不覆盖。
若目的是排查某个系统为何仍用 MD5/SHA-1,通常是兼容性约束(老数据、第三方对接),要先确认来源再谈替换。
实战片段
要点:① 按用途选算法 —— 完整性校验可用 CRC32/xxHash,对抗性场景必须 SHA-256 以上,密码绝不用通用哈希;② 自拼 hash(secret + data) 存在长度扩展攻击,密钥场景一律用 crypto.createHmac;③ 密码存储必须用带盐与成本参数的 KDF(Argon2id/bcrypt/scrypt/PBKDF2),通用哈希太快,GPU 每秒几十亿次;④ 任何 token 或签名比较都要用 timingSafeEqual,防时序侧信道;⑤ 迁移旧哈希用渐进方案,登录成功时顺手升级为 KDF 格式。
import { createHash, createHmac, randomBytes, scryptSync, timingSafeEqual } from 'crypto'
// ① 按用途选算法
const BY_PURPOSE = {
integrity: 'sha256', // 校验数据是否被改(可配 HMAC)
signature: 'sha256', // 对抗性场景:碰撞必须难构造
password: null, // 🔴 密码绝不用 createHash,走 KDF
dedup: 'xxhash64', // 仅做分片/去重,可以快
}
// ② 内容指纹:数据量大时可只取前 8 字节十六进制
function fingerprint(data, algo = 'sha256') {
const hex = createHash(algo).update(data).digest('hex')
return hex.slice(0, 16) // 16 字符 = 64 bit,亿级条目碰撞率可接受
}
// ③ 🔴 密钥场景必须用 HMAC,不能自己拼 hash(secret + data)
function mac(data, secret) {
return createHmac('sha256', secret).update(data).digest()
}
// ❌ 有长度扩展攻击:
// const bad = createHash('sha256').update(secret + data).digest('hex')
// 攻击者可算出 hash(secret + data + padding + extra) 的合法值
// ④ 密码存储:用 KDF + 随机盐,绝不用 sha256
function hashPassword(password) {
const salt = randomBytes(16)
const key = scryptSync(password, salt, 64, { N: 16384, r: 8, p: 1 })
return { salt: salt.toString('hex'), key: key.toString('hex') }
}
function verifyPassword(password, stored) {
const key = scryptSync(password, Buffer.from(stored.salt, 'hex'), 64, { N: 16384, r: 8, p: 1 })
// 定长比较,防时序侧信道
return timingSafeEqual(key, Buffer.from(stored.key, 'hex'))
}
// ⑤ 常量时间比较:任何 token / 签名校验都要用
function safeEqual(a, b) {
const ba = Buffer.from(a), bb = Buffer.from(b)
return ba.length === bb.length && timingSafeEqual(ba, bb)
}
// ⑥ 迁移旧哈希:登录成功时升级为 KDF
function upgradeIfNeeded(password, legacyMd5) {
const mine = createHash('md5').update(password).digest('hex')
if (!safeEqual(mine, legacyMd5)) return null
return hashPassword(password) // 登录时顺手换成 KDF 格式实践注解
- MD5 与 SHA-1 已不适用于安全场景两者都存在实用化的碰撞攻击(MD5 碰撞已可秒级构造),可用于校验传输完整性,但不可用于签名、口令或防篡改。安全场景一律 SHA-256 起步。
- 直接哈希口令等于把口令库送给攻击者无盐的 SHA-256 在 GPU 上每秒可试数十亿次。口令必须使用带工作因子的算法(bcrypt、scrypt、Argon2),并保证每个用户独立盐值;哈希与口令校验是两件不同的事。
- HMAC 不是「哈希加盐」HMAC = H(K ⊕ opad ‖ H(K ⊕ ipad ‖ m)),结构上防长度扩展攻击,而简单的 H(secret ‖ message) 存在长度扩展弱点。用哈希拼接密钥做签名是不安全的替代品。
数据来源
- NIST FIPS 180-4 (SHA family)
- RFC 1321 (MD5)
- RFC 9106 (Argon2)
- RFC 8018 (PBKDF2)
- FIPS 197 (AES)
- RFC 8017 (RSA)
上述编号、名称与定义均来自公开规范,本站不主张对规范数据本身的独创性;本页的编排方式、中文释义与实践注解由本站撰写。
| 算法 | 类型 | 输出 / 长度 | 安全性 | 最佳用途 |
|---|---|---|---|---|
| MD5 | 哈希 | 128 bit | 已破解 | 文件校验和(非安全用途) |
| SHA-1 | 哈希 | 160 bit | 已碰撞,弃用 | 旧系统兼容、Git 对象 |
| SHA-224 | 哈希 (SHA-2) | 224 bit | 安全 | 较少使用的 SHA-2 变体 |
| SHA-256 | 哈希 (SHA-2) | 256 bit | 安全 | 证书、区块链、数字签名(最常用) |
| SHA-384 | 哈希 (SHA-2) | 384 bit | 安全 | 高安全等级场景 |
| SHA-512 | 哈希 (SHA-2) | 512 bit | 安全 | 高安全等级场景 |
| SHA-3 | 哈希 (Keccak) | 224/256/384/512 bit | 安全 | SHA-2 的标准化备选 |
| RIPEMD-160 | 哈希 | 160 bit | 基本安全 | 比特币地址生成 |
| HMAC | 消息认证码 | 随底层哈希 | 安全(需密钥) | API 签名、webhook 校验 |
| bcrypt | 密码哈希 | 60 字符 | 安全(可调 cost) | 密码存储(支持广泛) |
| scrypt | 密码哈希 | 可变 | 安全(内存硬) | 抗 ASIC 的密码存储 |
| Argon2 | 密码哈希 | 可变 | 安全(现代首选) | 新项目密码存储首选 |
| PBKDF2 | 密码哈希 | 可变 | 安全(靠迭代) | 兼容性要求高的场景 |
| AES | 对称加密 | 128/192/256 bit | 安全 | 数据加密(推荐 GCM 模式) |
| RSA | 非对称加密 | 2048/4096 bit | 安全(密钥够长) | 密钥交换、数字签名 |
| ECC | 非对称加密 | 256 bit 起 | 安全(短密钥) | 移动端与 TLS,效率高 |
| TOTP | 一次性口令 | 6-8 位 | 安全(短时效) | 两步验证 2FA |
| CRC32 | 校验和 | 32 bit | 非加密 | 传输完整性校验 |
| Base64 | 编码(非加密) | 变长 | 非加密 | 二进制转文本传输 |
常见问题
MD5 还能用吗?
不能用于任何安全目的:MD5 已被证实可人为制造碰撞,可被伪造签名与证书。它只适合非恶意场景的文件完整性校验(如下载包校验)。安全用途应选 SHA-256 及以上。
密码存储该用什么算法?
必须用专门的密码哈希函数,优先级:Argon2(现代首选)> scrypt > bcrypt > PBKDF2。绝不能用 MD5、SHA-1、SHA-256 这类快速哈希裸存密码(即使加盐),因为它们计算太快、易被 GPU 暴力破解;盐值与 cost 参数交给成熟库处理。
哈希(Hash)和加密(Encrypt)有什么区别?
哈希是单向的:任意长度输入映射为固定长度摘要,无法反推原文,用于完整性校验与密码存储。加密是双向的:用密钥把明文变成密文,持有密钥可解密还原,用于保密传输与存储。Base64 只是编码,两者都不是。
SHA-256 和 SHA-3 该怎么选?
默认选 SHA-256:性能与生态最好,硬件加速普及,库支持最全。SHA-3 内部结构与 SHA-2 完全不同,适合做算法多样化备选(防止 SHA-2 未来被攻破时的单点风险)。二者当前都被认为是安全的。