存储密码时为什么要加盐?

存储密码时为什么要加盐?

本文转自 公众号 ByteByteGo,如有侵权,请联系,立即删除

今天来聊聊存储密码时为什么要加盐。

img

存储密码应注意

  1. 不要用纯文本存储密码,因为任何有内部访问权限的人都可以看到它们。
  2. 直接存储密码哈希值是不够的,因为它会受到预计算攻击,如彩虹表 (rainbow tables)。
  3. 为了减少预计算攻击,我们对密码进行加盐处理。

什么是盐?

根据 OWASP 指南,“盐是随机生成的唯一字符串,作为哈希过程的一部分添加到每个密码中”。

如何存储密码和盐?

  1. "盐"并不是为了保密,它可以以纯文本形式存储在数据库中。它用于确保每个密码的哈希结果是唯一的。
  2. 密码可以使用以下格式存储在数据库中:hash (password + salt)

如何验证密码?

要验证密码,可以通过以下过程:

  1. 客户端输入密码。
  2. 系统从数据库中获取相应的盐。
  3. 系统将盐附加到密码上,并对其求哈希值。我们把哈希值称为 H1。
  4. 系统比较 H1 和 H2,其中 H2 是存储在数据库中的哈希值。如果两者相同,则密码有效。

正文

在用户登录验证使用**加盐哈希(salted hash)**的密码,关键在于:**使用相同的盐值对用户输入的密码进行相同的哈希运算,并与数据库中存储的哈希值进行比较**。 --- ## ✅ 登录验证流程(加盐哈希) 1. **用户输入用户名和密码** 2. **系统根据用户名从数据库中查询用户信息** 3. **获取该用户存储的哈希密码和对应的盐值** 4. **使用该盐值对用户输入的密码进行加盐哈希** 5. **比较新生成的哈希值与数据库中存储的哈希值是否一致** 6. **一致则登录成功,否则失败** --- ## ✅ 示例代码(Java + SHA-256 + Salt) ### 假设数据库中存储了两个字段: - `stored_hash`:用户密码的哈希值 - `salt`:用于生成该哈希的盐值 ```java import java.security.MessageDigest; import java.util.Base64; public class PasswordVerifier { // 哈希方法(与注册一致) public static String hashPasswordWithSalt(String password, String salt) throws Exception { MessageDigest md = MessageDigest.getInstance("SHA-256"); md.update(salt.getBytes()); byte[] hashedPassword = md.digest(password.getBytes()); return Base64.getEncoder().encodeToString(hashedPassword); } // 验证登录 public static boolean verifyPassword(String inputPassword, String storedHash, String salt) throws Exception { String hashedInput = hashPasswordWithSalt(inputPassword, salt); return hashedInput.equals(storedHash); } public static void main(String[] args) throws Exception { // 模拟数据库中存储的值 String storedHash = "5Ld5r7ZzK4RrsGDe7JIh4SLfW7JpVWOeX6RrsGDe7JIh4SLfW7JpVWOe"; // 示例哈希值 String salt = "abc123xyz"; // 示例盐值 // 用户输入密码 String inputPassword = "myPassword123"; // 验证密码 if (verifyPassword(inputPassword, storedHash, salt)) { System.out.println("登录成功!"); } else { System.out.println("密码错误!"); } } } ``` --- ## ✅ 使用 Spring Security 的方式(推荐) 如果你使用的是现代密码哈希算法(如 `bcrypt`),**盐值是自动嵌入在哈希字符串中的**,你不需要手动处理盐值。 ### 示例:Spring Security + BCryptPasswordEncoder ```java import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; public class SpringLoginExample { public static void main(String[] args) { BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(); // 假设这是数据库中存储的哈希密码 String storedHash = "$2a$10$abc123..."; // 从数据库获取 // 用户输入的密码 String inputPassword = "myPassword123"; // 验证密码 if (encoder.matches(inputPassword, storedHash)) { System.out.println("登录成功!"); } else { System.out.println("密码错误!"); } } } ``` > **说明**:`BCryptPasswordEncoder.matches()` 方法会自动提取存储的哈希字符串中的盐值,并使用相同的算法重新计算哈希进行比较。 --- ## ✅ 总结 | 步骤 | 说明 | |------|------| | 1. 获取用户输入密码 | 用户在登录页面输入的密码 | | 2. 获取盐值和哈希 | 从数据库中根据用户名获取 | | 3. 重新计算哈希 | 使用相同算法和盐值对输入密码进行哈希 | | 4. 比较哈希值 | 若一致则登录成功,否则失败 | | 5. 使用 bcrypt 等现代算法 | 盐值自动嵌入在哈希字符串中,无需手动管理 | ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

xwhking

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值