Skip to content

第5篇: 密码存储与防护

第5篇:密码≠安全:现代密码存储与防护策略

在数字世界中,密码(Password)无疑是最普遍、最直观的身份认证方式。我们每天都在使用它们登录各种网站、应用和系统。然而,仅仅依靠密码来保障安全是远远不够的,甚至可以说,“密码”本身并不等同于“安全”。一旦密码被泄露或破解,攻击者便能轻而易举地冒充用户,造成无法估量的损失。

本篇文章将深入探讨现代密码的存储和防护策略,揭示为什么简单的密码已经无法满足安全需求,以及如何通过哈希、加盐、Pepper、慢哈希算法和严格的密码策略来构建更坚固的密码防线。

1. 为什么“密码≠安全”?——传统密码存储的缺陷

传统的密码存储方式简单粗暴,通常是将用户提交的密码明文存储在数据库中,或者使用一些弱加密算法进行保护。这种方式存在致命缺陷:

  1. 明文存储: 数据库一旦泄露,所有用户密码一览无余。
  2. 弱加密/可逆加密: 即使进行了“加密”,如果使用对称加密且密钥被泄露,或使用可逆哈希(如MD5、SHA1早期用途),攻击者也能轻易解密或反算出原始密码。

攻击者如何利用这些缺陷?

  • 拖库攻击: 攻击者获取到数据库后,直接拿到大量明文密码。
  • 彩虹表攻击: 针对已知明文-哈希对预先计算好的“彩虹表”,通过反查哈希值迅速找到对应明文密码。对于弱哈希算法,这种攻击效率极高。

为了应对这些挑战,现代密码存储和防护策略应运而生,其核心原则是:永远不要存储用户密码的明文! 而是存储密码的哈希值。

2. 密码哈希(Hashing):不可逆的指纹

含义: 密码哈希是将原始密码(明文)通过一个单向的数学函数(哈希算法)转换成一个固定长度的、看似随机的字符串(哈希值)。这个过程是“单向”的,意味着从哈希值几乎不可能逆向推导出原始密码。

工作原理:

  1. 用户设置密码 P
  2. 系统计算 H = Hash(P)
  3. 系统将 H 存储到数据库。
  4. 用户登录时,提交密码 P'
  5. 系统计算 H' = Hash(P')
  6. 比对 HH' 是否相同。若相同,则认证成功。

常见哈希算法(早期用于密码存储,但现在不推荐单独使用): MD5, SHA-1, SHA-256, SHA-512。

为什么单独哈希不足以安全?

虽然哈希是单向的,但如果用户密码简单且哈希算法速度快,攻击者仍然可以通过以下方式破解:

  • 字典攻击: 使用常见单词、短语、数字组合等构成的“字典”,逐一哈希后与泄露的哈希值比对。
  • 彩虹表攻击: 对大量常见密码预先计算哈希值,制作成巨大的查找表。一旦数据库泄露,攻击者可以直接查表找到密码。

3. 加盐(Salting):为每个密码定制“指纹”

为了对抗字典攻击和彩虹表攻击,加盐(Salting) 技术应运而生。

含义: “盐”(Salt)是一个随机生成的数据串。在哈希密码之前,将这个随机的“盐”与用户密码进行拼接,然后再进行哈希。

工作原理:

  1. 用户设置密码 P
  2. 系统为该用户生成一个唯一且随机Salt 值。
  3. 系统计算 H = Hash(P + Salt)
  4. 系统将 HSalt 一同存储到数据库。请注意,Salt 是不需要保密的,它与哈希值一起公开存储。
  5. 用户登录时,提交密码 P'
  6. 系统从数据库取出对应用户的 Salt 值。
  7. 系统计算 H' = Hash(P' + Salt)
  8. 比对 HH' 是否相同。

加盐的优势:

  • 对抗彩虹表: 由于每个用户的密码都使用了不同的随机盐,即使两个用户设置了相同的密码,其哈希值也不同。这使得攻击者无法预先计算彩虹表来破解大量密码。彩虹表攻击失效。
  • 对抗字典攻击: 攻击者每次尝试一个字典中的密码,都必须为每个用户重新计算哈希值(因为盐不同),这大大增加了攻击成本。

4. Pepper(胡椒):额外的全局秘密

尽管加盐能有效对抗彩虹表和个人字典攻击,但如果整个密码哈希数据库泄露,且攻击者拥有强大的计算能力,他们仍然可以对每个加盐后的哈希值进行暴力破解。为了增加额外的防御层,出现了 Pepper(胡椒) 的概念。

含义: “胡椒”是一个秘密的、全局性的密钥,它也被添加到密码和盐的组合中进行哈希。与盐不同,Pepper是保密的,不存储在数据库中,通常存储在独立的安全配置或密钥管理服务中。

工作原理:

  1. 用户设置密码 P
  2. 系统生成 Salt
  3. 系统从安全存储中获取 Pepper
  4. 系统计算 H = Hash(P + Salt + Pepper)
  5. 系统将 HSalt 存储到数据库。Pepper 不存储在数据库中。
  6. 用户登录时,提交密码 P'
  7. 系统从数据库取出对应用户的 Salt 值,从安全存储中获取 Pepper
  8. 系统计算 H' = Hash(P' + Salt + Pepper)
  9. 比对 HH' 是否相同。

Pepper的优势:

  • 对抗数据库完全泄露: 即使攻击者获取了整个数据库(包括加盐后的哈希值和盐),如果他们不知道Pepper,就无法计算正确的哈希值,从而无法破解密码。这为系统提供了一道额外的“紧急开关”。
  • 提高破解难度: 强制攻击者在离线破解时,必须同时猜测密码和Pepper,或通过其他方式窃取Pepper。

考虑因素: Pepper的安全性依赖于其存储和管理的安全性。如果Pepper被泄露,其防护作用将失效。

5. 慢哈希算法:抵抗暴力破解的核心武器

即使有了盐和Pepper,如果哈希算法的计算速度非常快(如MD5、SHA-256),攻击者依然可以通过每秒数亿甚至数十亿次的尝试来暴力破解简单密码。因此,现代密码存储的另一个关键是使用 “慢哈希算法”(Slow Hashing Algorithms)

含义: 慢哈希算法是专门为密码哈希设计的、计算成本高昂的哈希函数。它们通过引入大量迭代、内存消耗或并行计算限制,刻意减慢哈希过程。

目标: 显著增加攻击者进行暴力破解或字典攻击所需的时间和计算资源,使其在经济上不可行。即使每次计算只需要几毫秒,累积起来也能有效抵抗大规模攻击。

主流慢哈希算法:

  • BCrypt: 最早被广泛接受的慢哈希算法之一,基于Blowfish加密算法。它内置了“工作因子”(Work Factor)参数,允许管理员控制计算的迭代次数。迭代次数越高,哈希越慢,安全性越高,但也更消耗服务器资源。BCrypt的哈希值中包含了盐和工作因子,方便验证。
  • SCrypt: 比BCrypt更先进,除了迭代次数外,还引入了内存消耗参数(内存成本)。这使得SCrypt不仅计算缓慢,还需要大量的内存,从而有效抵抗GPU和ASIC等专用硬件的暴力破解。SCrypt的参数包括CPU/内存成本、块大小和并行因子。
  • Argon2: 获得2015年密码哈希竞赛冠军的算法,被认为是目前最强壮的密码哈希算法。它综合考虑了CPU时间、内存消耗和并行度,提供了三种变体(Argon2d、Argon2i、Argon2id)以适应不同场景的需求。Argon2d适用于对时间和内存都敏感的场景,Argon2i更适合抵抗侧信道攻击,Argon2id是两者的混合,推荐用于密码哈希。

如何选择?

  • BCrypt: 简单易用,兼容性好,对于大多数Web应用是可靠的选择。
  • SCrypt: 提供更好的内存抗性,适合对抗专业级攻击。
  • Argon2: 最先进和推荐的算法,提供最全面的保护,但实现复杂度可能略高。

在Java实践中: 许多安全库(如Spring Security)都内置了对这些慢哈希算法的支持,开发人员无需从头实现。

// 伪代码示例:使用Spring Security的BCryptPasswordEncoder
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

public class PasswordService {

    private final BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); // 默认强度10

    public String hashPassword(String rawPassword) {
        return passwordEncoder.encode(rawPassword);
    }

    public boolean matchesPassword(String rawPassword, String encodedPassword) {
        return passwordEncoder.matches(rawPassword, encodedPassword);
    }

    // 在实际应用中,可以通过构造函数注入或配置来调整强度
    // public PasswordService(int strength) {
    //     this.passwordEncoder = new BCryptPasswordEncoder(strength);
    // }
}

6. 密码策略(Password Policy):从源头提升强度

除了强大的存储策略,从用户设置密码的源头进行规范和限制也至关重要。密码策略是组织规定的一系列规则,旨在强制用户创建和维护强密码。

常见密码策略要素:

  • 最小长度: 例如,至少8位或12位。
  • 复杂性要求: 必须包含大小写字母、数字、特殊字符(至少三类或四类)。
  • 禁用常见密码/字典词: 禁止使用“password”、“123456”、“yourname”等弱密码。这需要结合黑名单或字典匹配。
  • 过期周期: 强制用户定期更换密码(但现代观点认为,过于频繁的过期可能导致用户设置更简单的密码,更推荐MFA而非严格过期)。
  • 历史密码禁用: 不允许使用最近N次用过的密码。
  • 账户锁定策略: 连续N次密码错误后,锁定账户一段时间或直到管理员解锁,以防止暴力猜解。
  • 密码泄露检测: 整合第三方服务(如HaveIBeenPwned),提醒用户其密码是否已在其他数据泄露中出现。

实施挑战: 过于严格的密码策略可能导致用户记忆困难,转而使用简单的模式或将密码写在纸上,反而降低安全性。因此,平衡安全性和用户体验至关重要。结合慢哈希算法和MFA,可以适当放宽一些密码策略的严格性,提高用户体验。

总结

密码安全是一个持续演进的战场。仅仅依靠明文存储或简单哈希已是过去式。现代的密码防护策略是一个多层次的防御体系:

  1. 哈希: 确保密码的不可逆性。
  2. 加盐: 对抗彩虹表和通用字典攻击,为每个用户提供独一无二的哈希值。
  3. Pepper: 提供额外的一层全局秘密保护,抵御数据库完全泄露后的离线破解。
  4. 慢哈希算法(BCrypt, SCrypt, Argon2): 大幅增加暴力破解和字典攻击的计算成本,使其在经济上不可行。
  5. 密码策略: 从源头规范用户行为,强制创建和维护强密码,并结合MFA等更高级认证手段。

通过综合运用这些策略,企业可以显著提升用户身份认证的安全性,有效抵御各种密码相关的攻击。在下一篇文章中,我们将继续探讨除密码之外的更高级认证方式——多因素认证(MFA)。