本文目录导读:

选择密码哈希算法是一个涉及安全性的关键决策,核心原则是:不要使用自己设计的算法,不要使用普通的快速哈希(如 MD5、SHA-1、SHA-256),而要使用专门为密码设计的、慢速且加盐的哈希算法。
以下是选择密码哈希算法的完整指南,按优先级从高到低排列:
第一梯队:强烈推荐(现代、安全、首选)
目前行业标准公认最安全、最推荐的算法只有两个:
-
Argon2
- 地位: 2015年密码哈希竞赛的获胜者,是目前最先进、最安全的密码哈希算法。
- 特点: 它可以同时抵抗三种攻击:
- GPU/ASIC(专用集成电路)攻击:通过内存密集型设计,让硬件无法并行加速。
- 时间-空间权衡攻击:通过CPU消耗和内存消耗的平衡性设计,让破解者无法通过牺牲内存来换取速度。
- 变体: 主要用 Argon2id(混合模式,推荐),其次是 Argon2i(抗侧信道)和 Argon2d(抗GPU,但可能被侧信道攻击影响)。
- 如何使用: 绝大多数现代密码库(如
libsodium,passlib,bcrypt的高阶包装)都支持 Argon2,你只需要提供密码、盐(库自动生成)和参数(cost、memory、parallelism)。
-
scrypt
- 地位: 在 Argon2 出现之前,它是针对硬件攻击的最佳选择,现在虽然稍逊于 Argon2,但在正确实现时仍然非常安全。
- 特点: 也是内存硬算法,强制消耗大量内存,从而大大增加定制硬件(ASIC)的破解成本。
- 适用场景: 如果库不支持 Argon2(很少见),scrypt 是极好的替代品,许多加密货币钱包(如 Litecoin)使用它。
第二梯队:仍然可用(经典、广泛支持、但已过时)
如果因为技术限制(例如使用非常老旧的语言或框架,无法安装新库)无法使用 Argon2 或 scrypt,可以考虑以下算法。但请注意,它们不如第一梯队安全。
-
bcrypt
- 地位: 曾经的王者,目前仍被大量生产系统使用。
- 特点: 基于 Blowfish 加密算法,慢速且可调成本(cost factor)。
- 缺点: 它不是内存硬的,因此可以被定制硬件(FPGA、ASIC)以相对较低的成本进行大规模破解,虽然比 MD5 慢很多,但和 Argon2/scrypt 相比,硬件加速的威胁要大得多。
- 建议: 如果你必须选择 bcrypt,请将 cost factor 设置得尽可能高(在服务器可接受的延迟内,建议至少 10-12,例如成本因子为 12 时,单次哈希约需 250ms)。
-
PBKDF2
- 地位: 行业标准(RFC 2898),广泛使用在 WPA3、iOS/Android 密码管理等。
- 特点: 简单、可调迭代次数。
- 致命弱点: 纯粹依赖CPU,没有内存硬性要求,这意味着使用 GPU 或 ASIC 可以非常高效地并行破解,它比 bcrypt 更脆弱。
- 建议: 除非你的平台只支持 PBKDF2(例如某些嵌入式系统、旧版PHP),否则尽量避免,如果必须用,将迭代次数设为极高(600,000次以上,这会使单次哈希达到数百毫秒级)。
第三梯队:绝不要使用(危险)
这些算法绝对不要用于存储用户密码:
- MD5:已经被完全破解,可以极快地生成碰撞(不同密码相同哈希),并且速度极快,每秒可尝试数十亿次。
- SHA-1:同样被攻破,速度极快,已不推荐用于任何安全场景。
- SHA-256 / SHA-3系列:虽然它们是优良的通用哈希算法(用于校验文件完整性、数字签名等),但它们是为速度优化的,正是这个特性使它们极其不适合密码存储,攻击者可以用 GPU 在几小时内尝试所有8字符纯字母密码。
- 不加盐的哈希:任何算法如果不加随机的盐,都会导致两个用户密码相同时哈希完全相同,极易被彩虹表攻击。
核心参数最佳实践
无论你选择 Argon2、scrypt 还是 bcrypt,都需要合理设置参数:
-
盐(Salt):
- 必须由系统自动生成,使用高性能的随机数生成器(
os.urandom或SecureRandom)。 - 长度至少16字节(128位)。
- 每个用户每个密码都不同,并且存储在哈希值旁边(通常是哈希值的一部分)。
- 必须由系统自动生成,使用高性能的随机数生成器(
-
工作因子(Cost / Iterations):
- 目标: 在你的生产服务器硬件上,单次哈希计算时间大约在 100ms - 300ms 之间(用户注册/登录时能接受这个延迟)。
- 方法: 在部署前在你的服务器上进行基准测试,如果你选择 Argon2,调整
time_cost(时间/CPU成本)和memory_cost(内存大小,64MB~128MB);对于 bcrypt,调整cost因子(如 12~14);对于 PBKDF2,调整迭代次数(如 几十万到几百万次)。
-
算法版本管理:
- 当未来更强的算法出现时,你需要能够在不破坏现有用户登录的情况下升级。
- 策略: 在存储的哈希字符串中包含算法标识符(
$argon2id$v=19$m=65536,t=2,p=4$...或$2y$12$...)。 - 当用户登录时,检查所用的算法是否为当前最新,如果不是,使用新算法重新哈希并更新数据库(称为“密码哈希升级”)。
总结决策(快速选择表)
| 场景 | 推荐算法 | 理由 |
|---|---|---|
| 全新项目,无技术限制 | Argon2 (Argon2id) | 最安全,抗硬件攻击,有内存硬性要求。 |
| 需要兼容旧系统或库限制 | scrypt | 安全程度高,也是内存硬,但略逊于 Argon2。 |
| 必须使用经典库(如某些PHP/MySQL) | bcrypt (成本因子 >=12) | 比 PBKDF2 安全,但不如前两者。 |
| 唯一选择(嵌入式/极旧系统) | PBKDF2 (迭代次数 >=60万) | 安全性最低,但比不使用任何哈希好。 |
| 永远不要使用 | MD5, SHA-1, SHA-256 | 太慢!不,是太快!** |
如何确保正确实现
- 使用成熟的库:永远不要自己写密码哈希代码,使用如下库:
- Python:
bcrypt,argon2-cffi,hashlib(内置PBKDF2) - Node.js:
bcryptjs,argon2,crypto(内置 scrypt, PBKDF2) - Go:
golang.org/x/crypto/bcrypt,argon2,scrypt - Ruby:
bcryptgem,argon2gem - PHP:
password_hash()(默认使用 bcrypt + 自动盐,8.0+ 可选 Argon2) - Java:
Spring Security的BCryptPasswordEncoder,Argon2PasswordEncoder
- Python:
请记住:没有完美,只有更优,定期关注安全界的最新动态(比如几年后 Argon2 是否被攻破),并在可行时升级你的哈希策略。