返回列表
哈希加盐与密钥派生函数
为什么需要加盐?
直接对密码进行哈希(如SHA-256)存储存在风险:
- 彩虹表攻击:攻击者可以预计算常见密码的哈希值,形成彩虹表,快速反向查询。
- 相同密码可识别:如果多个用户密码相同,哈希值也相同,泄露后攻击者可推测密码相同。
- 并行破解:攻击者可以同时对多个哈希值尝试一个密码,提高效率。
加盐(Salting)是在哈希前为每个用户附加一段随机数据(盐),有效防御上述攻击。即使两个用户使用相同密码,加盐后的哈希值也完全不同。
加盐的最佳实践
- 使用足够长的随机盐:建议至少16字节(128位),使用密码学安全的随机数生成器(如PHP的random_bytes、OpenSSL的RAND_bytes)。
- 每个用户独立盐:即使同一用户更改密码,也应生成新盐。
- 盐与哈希一起存储:通常将盐与哈希拼接存储,或单独字段,便于验证时使用。
- 结合慢哈希算法:仅加盐还不够,应使用计算成本高的算法(如PBKDF2、bcrypt、Argon2)来抵抗暴力破解。
密钥派生函数(KDF)
密钥派生函数专门用于从低熵密码派生高强度密钥,内置加盐和迭代计算,是密码存储的推荐方案。
1. PBKDF2
基于密码的密钥派生函数2,通过多次迭代哈希(如HMAC-SHA256)增加计算成本。参数包括密码、盐、迭代次数、输出长度。
// PHP示例(使用hash_pbkdf2)
$hash = hash_pbkdf2('sha256', $password, $salt, 100000, 32);
// 存储格式:hash:salt:iterations
2. bcrypt
基于Blowfish密码的慢哈希函数,内置加盐,成本因子可调(如10表示2^10轮)。输出包含算法版本、成本因子、盐和哈希。
// PHP示例(推荐)
$hash = password_hash($password, PASSWORD_BCRYPT, ['cost' => 12]);
// 验证
if (password_verify($password, $hash)) { ... }
3. Argon2
2015年密码哈希竞赛冠军,提供抗GPU攻击的内存硬函数。支持Argon2id(混合模式)是最佳实践。
// PHP 7.2+ 支持
$hash = password_hash($password, PASSWORD_ARGON2ID);
常见误区
- 盐太短或固定:固定盐等同于没有加盐;短盐(如4字节)易被预计算。
- 盐未与哈希一起存储:验证时需要相同的盐。
- 只加盐不慢哈希:GPU加速的暴力破解仍可能快速破解短密码。
- 盐与密码简单拼接而不使用KDF:可能导致长度扩展攻击或碰撞风险。
- 自行实现KDF:应使用经过广泛审查的标准库,避免安全漏洞。
实践建议
- 优先使用PHP的
password_hash()和password_verify(),自动处理加盐和算法选择。 - 如果必须使用SHA系列,应至少做10000次迭代(如PBKDF2),并存储迭代次数。
- 定期升级算法(如从bcrypt迁移到Argon2),验证时使用
password_needs_rehash()。
总结
加盐和慢哈希是安全存储密码的基石。开发者应使用成熟的密钥派生函数,避免自行发明,确保用户密码安全。