散列与加密
bcrypt和blowfish最大的不同就是bcrypt是对称加密算法,bcrypt是散列算法。(这是一种特殊的散列算法,但我们稍后会谈到。)
加密(Blowfish 所做的)
对称加密算法采用密钥和一些明文(经常使用术语“消息”并且通常使用变量m来指代它)并输出密文c。这个想法是密文只能用加密时使用的相同[^same]密钥转换回原始消息。
[^same]:我在这里给出近似真实的答案,以关注中心思想和概念。我希望那些认识到我的答案不完全正确的人会在说明性的目的上让我放松一下。
所以 Alice 可能用密钥 13 加密消息“黎明攻击”(这不是河豚,而是对称加密)并产生密文“Nggnpx ng qnja”。她可以将该密文发送给 Bob,如果 Bob 知道密钥是 13,他可以将密文转换回原始消息。有时,Alice 不想向其他人发送消息,但她想将消息存储起来供自己以后阅读。我们可以将这些情况视为 Alice 向未来的 Alice 发送消息。
使用 Blowfish(而不是我在示例中使用的加密),任何没有密钥的人实际上都不可能从密文中了解有关原始消息的任何信息(除了它的长度和存在)。顺便说一句,AES 是比 Blowfish 更可取的对称加密算法,但我将继续在我的描述中使用 Blowfish,因为这是被问到的。
散列
加密哈希算法(bcrypt 是一种特殊的算法,有一些额外的功能,但我将从更简单的情况开始)不需要密钥并且实际上不可逆。因此,像 SHA256 这样的加密哈希算法会接收像“JaguarsRule!”这样的消息(比如说密码)。并将其转换为哈希。
$ echo -n 'JaguarsRule!' | shasum -a256
13f26e5a60e402d895c5ce1d9492d080563c5079a8b5f52a25953fd24a2cb1da
加密哈希的几个属性之一是您无法计算“JaguarsRule!”。从13f26e5a60e402d895c5ce1d9492d080563c5079a8b5f52a25953fd24a2cb1da. 在这种情况下没有钥匙。
因此,像这样直接的散列机制似乎是检查服务器密码的好方法。假设您正在运行一些让人们登录的 Web 服务。您有一个用户,假设用户名jason使用密码设置他的帐户JaguarsRule!。因为您不希望任何可以读取您服务器上的内容的人知道 Jason 的密码,所以您不存储密码本身。相反,您存储其哈希值。
当 jason(或伪装成 jason 的人)登录您的服务器时,获取输入的密码并对其执行散列操作并比较存储的散列。如果哈希匹配,则系统知道输入了正确的密码。
盐和慢煮
上面描述的方案,使用像 SHA256 这样的通用加密散列,效果很好,除了一件事:人们选择可猜测的密码。如果人们像wZFoO_SKrgEw他们一样使用密码,JaguarsRule!那么我们就不需要像 bcrypt 这样的专门的散列函数。但是,作为人类的人,不会在足够大的空间内随机且一致地选择密码。相反,他们更有可能选择一些可能的密码而不是其他密码。
盐
考虑您系统的另一个用户,pillboy. 药丸男孩也是杰克逊维尔美洲虎队的粉丝,并且恰好也被JaguarsRule!选为他的密码。使用(未加盐的)散列,Pill Boy 密码的散列将与 Jason 密码的散列相同。攻击者只需要比较哈希值就可以看到这两个用户的密码相同。
此外,如果攻击者以某种方式发现 Jason 的密码是 JaguarsRule!`,那么她会立即知道 Pill Boy 的密码。此外,她可能会为最常见的密码创建一个大的哈希列表。
解决这个问题的方法是在散列密码之前用一些独特的东西给密码加盐。因此,当第一次创建 Jason 密码的哈希时(当 Jason 第一次注册并创建密码时),您会向其中添加一些随机内容。假设此时您的系统创建了 salt 9cb3779f69e271c1。然后它在散列之前将其粘贴在密码前面,所以它实际上是在散列类似的东西9cb3779f69e271c1JaguarsRule!(这不是盐的实际添加方式,但这足以解释盐的用途。)。的哈希9cb3779f69e271c1JaguarsRule!将与 的哈希完全不同JaguarsRule!。
当需要验证密码时,系统将需要知道盐是什么。因此它将存储盐和哈希以及 Jason 的用户名。当 Pill Boy 创建他的帐户时,它将设置一个不同的随机生成的盐,因此他的盐和密码组合将产生不同的哈希。
因此,您存储在服务器上的内容可能如下所示
[
{
"algorithm": "sha256",
"username": "jason",
"salt": "9cb3779f69e271c1",
"hash": "37a932267ed055facf03cc5d09ca90f927a1eed47a8cd4856e57cd67434426be"
},
{
"algorithm": "sha256",
"username": "pillboy",
"salt": "0a19471dab710025",
"hash": "4be637a8c85455dce0cdc1c7670f062764100276b6ed64141c06fbef4578f185"
},
]
通过为这些用户设置不同的盐,我们无法看到他们具有相同的密码,并且攻击者将无法将哈希值与她预先计算的常用密码哈希值列表进行比较。
所以 bcrypt,因为它是为密码散列设计的,所以需要一个盐。
慢慢来
加盐是绝对必要的,但它仍然无法阻止获取服务器上存储内容的攻击者自动猜测。她可以获取盐并将其与常用密码和计算哈希相结合,直到找到匹配项。她可能每秒可以执行数千万或数亿次密码猜测。
她能够如此快速地计算出许多猜测的原因之一是,像 sha256 这样的加密哈希算法被设计为快速高效,同时仍能满足其安全属性。所以像 bcrypt 这样的事情是“慢散列”。这个想法是,从一些消息(盐和密码)到散列可能需要大量计算,以至于典型的计算机需要四分之一秒来执行散列。
用户不会注意到四分之一秒的延迟,但如果攻击者使用的是同一类型的计算机,那么她最终会减少到每秒进行四次猜测,而不是一千万次。(在实践中,她会有专门配置的计算设备,但将她从每秒几百万次猜测减慢到每秒几万次猜测是一件好事。)
bcrypt 是几个“慢散列”之一。其他的是 PBBKDF2、scrypt 和 Argon2。最后两个不仅需要大量计算来创建散列,而且还需要大量内存。
Bcrypt 很棒,但是...
加盐和慢散列是每个密码散列系统都应该做的事情。但它也会产生递减的收益。慢速哈希可以为防御者做很多事情,因此我们仍然需要(只要存在密码)让人们使用更难猜的密码。我在Bcrypt 中写过更多关于这方面的文章很棒,但是......
注意:感谢https://security.stackexchange.com/users/6253/schroeder正确删除了我之前的答案,我在其中字面意思是“就像我 5 岁”。我向原始海报道歉,因为我很刻薄而不是有帮助。