在 Java 中对加密(尤其是密码哈希)加盐(Salt) 的核心目的是增强安全性,主要应对以下场景和攻击:
1. 防御彩虹表攻击(Rainbow Table Attacks)
- 问题:相同的密码经过哈希后会产生相同的结果(如
MD5("123456")
始终相同)。 - 攻击:攻击者预先计算海量密码的哈希值(彩虹表),直接反向查询破解。
- 加盐解决:为每个密码生成唯一随机盐值,使相同密码的哈希结果不同。
// 示例:加盐后的哈希值 = hash(盐 + 密码) hash("A1b2C3" + "123456") ≠ hash("X7y8Z9" + "123456")
- 效果:攻击者无法复用预先计算的彩虹表。
2. 防止用户密码重复暴露
- 问题:多个用户使用相同密码时,数据库中出现重复哈希值(泄露一个等于泄露所有)。
- 加盐解决:每个用户分配独立盐值,相同密码的哈希结果不同。
用户A:盐="A1b2", 哈希值=hash("A1b2+password") 用户B:盐="X7y8", 哈希值=hash("X7y8+password") // 结果不同
- 效果:攻击者无法批量识别使用相同密码的用户。
3. 增加暴力破解难度
- 问题:简单密码的哈希值易被穷举破解(如
hash("000000")
)。 - 加盐解决:盐值扩展了密码长度和复杂度,迫使攻击者针对每个用户单独破解。
// 盐值使实际输入变复杂 原始输入: "123456" 加盐后输入: "9fKd!2mW+123456" // 暴力破解成本剧增
- 效果:显著拖慢大规模自动化破解。
4. 符合安全存储规范
- 行业标准(如 OWASP、NIST)要求密码存储必须使用盐值。
- 常用算法(如
PBKDF2
、bcrypt
、scrypt
)内置盐值支持。
Java 中加盐的最佳实践
import java.security.SecureRandom;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
public class PasswordUtils {
// 生成盐值(推荐长度 16-32 字节)
public static byte[] generateSalt() {
SecureRandom random = new SecureRandom();
byte[] salt = new byte[16];
random.nextBytes(salt);
return salt;
}
// 使用 PBKDF2 加盐哈希密码
public static byte[] hashPassword(char[] password, byte[] salt) throws Exception {
PBEKeySpec spec = new PBEKeySpec(
password,
salt,
10000, // 迭代次数,增加计算成本
256 // 输出密钥长度
);
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
return factory.generateSecret(spec).getEncoded();
}
// 验证密码
public static boolean verifyPassword(
char[] inputPassword,
byte[] storedHash,
byte[] storedSalt
) throws Exception {
byte[] testHash = hashPassword(inputPassword, storedSalt);
return Arrays.equals(storedHash, testHash);
}
}
存储盐值的正确方式
- 将盐值和哈希值一起存储在数据库中(无需保密)。
- 格式示例:
| user_id | password_hash | salt | |---------|-----------------------------------|--------------------| | 1001 | 2a5d7f... (hex) | d9c2a3... (hex) |
不需要加盐的场景
- 加密传输数据(如 TLS/SSL)。
- 对称加密(如 AES)——盐值主要用于密码哈希而非通用加密。
总结:何时必须加盐?
场景 | 是否需加盐 | 原因 |
---|---|---|
存储用户密码 | ✅ 必须 | 防止彩虹表攻击、批量破解 |
敏感数据哈希(如令牌) | ✅ 推荐 | 增加唯一性和复杂性 |
传输数据加密(如 HTTPS) | ❌ 不需要 | 由协议层保证安全 |
对称加密(如 AES) | ❌ 不需要 | 盐值不适用于密钥派生以外场景 |
💡 关键原则:只要涉及用户密码存储,必须使用加盐的强哈希算法(如
PBKDF2
、bcrypt
或Argon2
)。