Skip to content

如何安全的存储用户密码

引言

你是否好奇过,你在各种平台设置的账号密码,是如何被存储的?你在新闻中听到的那些“某某公司用户密码泄露”,黑客又是如何做到的?其实,这是一场持续进行的技术博弈——一方是保护密码安全的工程师,另一方则是不断试图破解密码的黑客。对于工程师来说,看似简单的密码存储任务,实际上充满挑战。在接下来的内容中,我们将深入探讨这些存储密码的技术,从最基本的做法开始,一步步揭示工程师是如何通过巧妙的设计和安全措施,保证密码安全的。

明文存储

在互联网早期,一些工程师认为,既然有服务器和数据库的安全防护作为双重保障,就没有必要再对用户密码进行加密处理了。所以他们选择了简单粗暴的做法,直接将用户密码以明文的形式存储在数据库中。然而,这种轻视安全的做法存在巨大的隐患。由于许多用户为了方便记忆,往往在不同网站使用相同或相似的密码,这导致黑客一旦攻破了数据库,就可以利用这些密码去尝试访问用户在其他平台上的账号。进一步扩大了安全风险。因此,对用户密码进行加密存储是至关重要的。

加密存储

有了此类教训之后,工程师意识到,直接存储用户的明文密码是不安全的,需要给密码加密。

于是,工程师设计了一种方法,通过一把密钥将用户密码“上锁”。

当用户输入密码时,程序会使用密钥对密码进行加密,生成一串看似随机的字符,这样即便黑客获得了加密后的密文,也无法直接解读出原始密码。

听起来很不错对吧,但这种方案有一个重要特征:它是可逆的。也就是说,只要拥有密钥,你就可以将加密后的字符解密,恢复原始密码。这就像你给自己的房间加了一把锁,虽然门看起来很安全,但如果有人偷到了钥匙,他们就可以轻松打开门,进入你的房间。

想想看,如果黑客拿到了这个密钥,不就能开锁还原出原始密码了吗?你可能会说,那把密钥藏起来不让黑客发现不就行了。你说的很对,工程师的确这么做了,通常他们为了安全起见会把密钥和用户信息分开存放,不放在一起。但是想要做到密钥百分之百不泄露,几乎是不可能的。因此这种方案也不太保险。

哈希加密

那么,如何在不需要密钥的情况下也能安全地保存密码呢?工程师们把视线转向了一种叫做哈希(Hash)算法的技术。

哈希算法就像一个“黑箱子”,你可以把任意长度的信息——无论是照片、软件,还是你的密码——放进这个箱子里。经过箱子的转换处理,给你输出一段固定长度的、看似随机的代码。

这种代码很可能你曾经见到过,例如软件的安装包。我们在网站上下载软件的时候,经常看到下载页显示的哈希,就是下图红框部分:

sha

红框中开头的MD5就是一种哈希算法的名称,对,哈希算法不止一种。后边的代码就是经过 md5 哈希算法算出来的哈希值。

我们下载软件安装包后,可以检查下载后的文件的哈希,是否对的上官网的哈希,哈希一致则说明下载的过程中,原始数据没有被人篡改,从而避免下载到一些恶意的软件或者病毒等。这就是哈希算法一开始的作用:数据校验。

然而,哈希算法不仅仅适用于数据校验。它的一些关键特点使其非常适合拿来保护用户密码。

具体来说,哈希算法有以下几个特点:

  1. 转换单向性:给定一个哈希值,没办法计算出此哈希值对应的输入
    1. 这意味着一旦你将数据转化为哈希值,就无法从哈希值反推回原始数据。换句话说,哈希算法只能从输入生成输出,但不能从输出反推出输入。
    2. 这使得哈希算法非常适合存储用户密码。即使黑客设法获取了数据库中的哈希值,他们也无法轻易地推导出原始密码。这种单向性为密码提供了一层额外的安全保障,即便数据泄露,用户的密码仍然具有一定的安全性。
  2. 转换确定性:相同的输入,总会产生相同的输出
    1. 这意味着无论你运行多少次相同的哈希算法,算出来的哈希值结果都是一样的。
    2. 工程师能利用这一点将用户密码转为哈希值存储在数据库中,当用户登录时,输入的密码会被哈希处理后与数据库中的哈希值进行比较,如果二者相同,用户的密码就是正确的,登录也就成功了;如果不同,登录则失败。
  3. 输入敏感性:相似的输入,会产生完全不同的输出
    1. 这意味着即使用户的密码中有一个字符不同,生成的哈希值也会完全不同。
    2. 这让黑客完全找不到密码规律,从而大大增加了破解的难度。

正因为这些特点,工程师们广泛采用了像 MD5、SHA-1、SHA-2 这样的哈希算法来保护用户密码。

然而,黑客们并没有因此放弃,他们开始寻找新的方法来破解这些哈希值。

字典攻击的诞生

虽然哈希算法的单向性和输入敏感性让直接反推密码变得几乎不可能,但黑客们意识到,可以通过一种巧妙的方法来绕过这些限制。他们利用预先收集的大量常见密码(比如常用词汇、常见姓名、日期、数字序列等),将这些密码通过哈希算法生成相应的哈希值,构建出一个巨大的“密码字典表”。这个表中记录了明文密码及其对应的哈希值。

当黑客设法侵入了某个数据库后,他们只需将数据库中存储的哈希值与自己的字典表逐个比对,如果找到了匹配的哈希值,他们就可以直接得出对应的明文密码。通过这种方式,黑客能够绕过哈希算法的单向性,尤其对那些使用常见、简单密码的用户,字典攻击显得异常有效。

彩虹表的引入

这还没完,随着破解技术的发展,黑客们还升级了高阶版“字典表”——彩虹表。它做了哪些升级呢?

我们假设将哈希后的密文比作一把锁,而破解这个锁的过程就是找到一把合适的钥匙。字典攻击就像是黑客预先制作好所有可能的钥匙,然后全部带到现场逐一尝试,这样虽然节省了制作钥匙的时间,但携带这么多钥匙本身又非常麻烦。

彩虹表的不同之处在于,它并不需要带上所有的钥匙。相反,它将这些钥匙按照某种规律进行分组,每组中只携带最具代表性的几把“特征钥匙”。当发现某个“特征钥匙”差一点就能开锁时,它会现场对该钥匙进行简单的打磨和调整,直到能够打开这把锁。这样一来,彩虹表既减少了存储空间的需求,又保留了破解密码的能力。

简单来说,彩虹表比字典表更加聪明和高效。它通过减少存储需求,使用一种更巧妙的方式链接和推测出密码,最终达到与字典攻击类似的效果,更适用于大规模密码破解。

加盐策略

那怎样才能抵御字典表和彩虹表的攻击呢?工程师们想出了给密码“加盐”的策略。这种策略的具体做法是:

当用户注册时,不直接对每个密码进行哈希处理,而是先随机分配一个称为 “盐” 的随机字符串,然后把它和用户密码进行组合,再进行哈希,并把盐和哈希密码一起存入数据库。

当用户登录时,从数据库取出用户的盐和哈希密码,用同样的规则把盐和用户提供的密码进行组合,再进行哈希,然后对比存储的哈希密码是否与这次哈希后的结果匹配,相等则登录成功,反之失败。

这种做法有效废掉了黑客原有的字典表和彩虹表,因为他们的字典表只包含了非加盐算出来的哈希值。例如数据库中用户的密码和盐是这样,由于黑客数据库中只包含非加盐的哈希值密码,导致虽然明文密码和哈希算法一样,但两者不再匹配。这还有个额外的好处就是现在具有相同密码的用户,他们的哈希值密码不同了,而在没加盐之前,他们的哈希值是相同的。

可是,就连这样的加密措施都有可能不保险,因为随着计算机硬件性能的飞速提升,黑客们开始利用高性能 GPU 进行暴力破解,尝试逐一测试每种可能的密码加盐的组合。这就像黑客们制造了一台超级快速的钥匙制造机,每秒可以尝试数十亿种钥匙组合。这让加盐后的锁也变得有可能被强行打开。

慢哈希算法

为了解决这一问题,工程师们转向了“慢哈希”算法,如 Bcrypt。慢哈希算法的核心思想是,通过设计让每次哈希计算的过程都变得非常长,并且在计算中增加内存消耗,从而显著增加黑客暴力破解的难度。

我们来举一个具体的例子,假设我们有一个用户密码 P@ssw0rd123,现在我们来看看 bcrypt 是如何处理它的:

  • 生成盐(Salt)

    • bcrypt 首先生成一个随机的盐。例如,假设生成的盐是 sA1t$4mpl3.
    • 盐的长度通常是 128 位(约 16 个字符)。
  • 合并密码和盐

    • bcrypt 将用户的密码和生成的盐合并在一起。比如:

      shell
      sA1t$4mpl3P@ssw0rd123
  • 多轮迭代哈希计算

    • bcrypt 对合并后的字符串进行多轮哈希计算,假设默认是 10 轮。
    • 每一轮哈希计算都基于前一轮的结果,意味着每一轮都会消耗一定的计算资源和时间。
  • 并行计算抵抗:

    • bcrypt 算法对内存访问进行了严格的控制,以防止利用并行计算加速破解。
  • 生成最终哈希值

    • bcrypt 最终生成的哈希值可能是这样的:

      shell
      $2a$10$sA1t$4mpl3f0A1jgE0.pXJ7XM2z9FE1UY9dL8KCl3Xw5Q8FRpOUM5LPv
    • 这个哈希字符串包括了以下几部分:

      • $2a$:bcrypt 的版本信息。
      • 10:迭代的次数(在这里是 10 轮)。
      • sA1t$4mpl3:使用的盐。
      • f0A1jgE0.pXJ7XM2z9FE1UY9dL8KCl3Xw5Q8FRpOUM5LPv:加盐并经过多轮哈希后的密码哈希值。
  • 存储哈希值

    • bcrypt 最终生成的哈希字符串存储在数据库中。当用户下次登录时,系统会使用相同的盐和相同的迭代次数对输入的密码进行相同的处理,并比较结果是否与存储的哈希值匹配。

通过这样一套随机盐和多轮哈希的组合拳, 能够有效抵御字典表和彩虹表的攻击。这就像黑客拿到了一把锁,即使他们有制作钥匙的机器,这个机器因为锁的工艺难度较大,也变得非常慢,每制造一把钥匙都需要大量时间,而锁的设计又很复杂,必须多次尝试。这样,破解锁的时间和成本就大大增加了。

双因素认证(2FA):进一步加强安全

尽管“慢哈希”算法已经极大地提升了密码的安全性,但在面对高性能计算资源或潜在的漏洞时,密码保护的单一防线依然有可能被攻破。为了解决这个问题,工程师们提出了“双因素认证”(2FA)这一更为安全的认证方式。

双因素认证(2FA)是指在用户登录时,不仅需要提供密码(第一因素),还需要通过另一种方式(第二因素)来验证用户的身份。第二因素通常是用户随身携带的设备或生物特征信息,比如手机上的验证码、指纹或面部识别等。

我们可以将 2FA 比喻成一把双重锁的安全机制:

  1. 第一把锁——密码:这把锁依然是由用户密码构成的。无论密码被多么精心地加密,假如黑客已经破解了这把锁,意味着第一道防线被突破。
  2. 第二把锁——动态验证码:2FA 的关键在于第二把锁。这把锁的钥匙是一串每次登录时动态生成的验证码。例如,用户输入正确的密码后,还需要从手机或者邮箱上获取一串临时验证码来解锁第二把锁。这个验证码通常在几分钟内有效,过期即失效。

这种双重锁的设计确保了,即使黑客得到了用户的密码,也无法仅凭密码登录账号,因为他们还缺少第二把锁的钥匙——动态验证码或生物特征信息。

总结回顾

在整个密码保护的历史中,工程师们不断地升级和优化防护措施,以应对黑客越来越复杂的攻击手段。让我们回顾一下这一路走来的历程:

  1. 明文存储:最初的密码存储方式非常不安全,黑客一旦获取了数据库,就可以轻易查看所有用户的密码。
  2. 加密存储:工程师们尝试用加密存储的方式保护密码,但这种方法在密钥泄露时同样脆弱。
  3. 哈希算法:通过哈希函数将密码转化为固定长度的字符串,这样即使黑客获取了哈希值,也无法直接得到密码。
  4. 哈希+盐:为了抵御字典表和彩虹表攻击,工程师们引入了“加盐”策略,每个用户的密码都被随机盐值加强,从而使得同一密码在不同用户之间的哈希值不同。
  5. 慢哈希:为了进一步防止暴力破解,bcrypt 等慢哈希算法通过多轮迭代,增加了破解的时间和计算成本,使得黑客即使使用强大的计算资源,也难以快速破解密码。
  6. 2FA 双因素认证:最终,工程师们开发了 2FA 双因素认证,不仅要求密码,还需要通过第二个独立的验证因素,如动态验证码或生物特征,进一步增强了账号的安全性。
## 结语

好了,这就是今天的全部内容了!希望现在的你对密码的那些防护措施有了更清楚的了解。最后需要提醒的是,虽然现在大多数网站都会对密码进行了加密处理,但这并不意味着你可以掉以轻心。尽量避免使用简单的密码,也不要在所有账户中使用相同的密码。这样一旦某个账户的密码被破解,其他账户也可能处于风险之中。

上次更新: 2024-09-17