本文翻译自:How can bcrypt have built-in salts?
Coda Hale's article "How To Safely Store a Password" claims that: Coda Hale的文章“如何安全地存储密码”声称:
bcrypt has salts built-in to prevent rainbow table attacks. bcrypt内置了盐来防止彩虹表攻击。
He cites this paper , which says that in OpenBSD's implementation of bcrypt
: 他引用了这篇论文 ,其中说在OpenBSD的bcrypt
实现中:
OpenBSD generates the 128-bit bcrypt salt from an arcfour (arc4random(3)) key stream, seeded with random data the kernel collects from device timings. OpenBSD从arcfour(arc4random(3))密钥流生成128位bcrypt salt,并使用内核从设备计时收集的随机数据进行种子处理。
I don't understand how this can work. 我不明白这是如何工作的。 In my conception of a salt: 在我的盐概念中:
- It needs to be different for each stored password, so that a separate rainbow table would have to be generated for each 每个存储的密码需要不同,因此必须为每个密码表生成一个单独的彩虹表
- It needs to be stored somewhere so that it's repeatable: when a user tries to log in, we take their password attempt, repeat the same salt-and-hash procedure we did when we originally stored their password, and compare 它需要存储在某个地方以便它是可重复的:当用户尝试登录时,我们会尝试密码,重复我们最初存储密码时所做的相同的盐和哈希过程,并进行比较
When I'm using Devise (a Rails login manager) with bcrypt, there is no salt column in the database, so I'm confused. 当我使用带有bcrypt的Devise(一个Rails登录管理器)时,数据库中没有salt列,所以我很困惑。 If the salt is random and not stored anywhere, how can we reliably repeat the hashing process? 如果盐是随机的并且没有存储在任何地方,我们如何可靠地重复散列过程?
In short, how can bcrypt have built-in salts ? 简而言之, bcrypt如何内置盐 ?
#1楼
参考:https://stackoom.com/question/SfQj/bcrypt如何内置盐
#2楼
This is from PasswordEncoder interface documentation from Spring Security, 这是来自Spring Security的PasswordEncoder接口文档,
* @param rawPassword the raw password to encode and match
* @param encodedPassword the encoded password from storage to compare with
* @return true if the raw password, after encoding, matches the encoded password from
* storage
*/
boolean matches(CharSequence rawPassword, String encodedPassword);
Which means, one will need to match rawPassword that user will enter again upon next login and matches it with Bcrypt encoded password that's stores in database during previous login/registration. 这意味着,需要匹配用户将在下次登录时再次输入的rawPassword,并将其与在先前登录/注册期间存储在数据库中的Bcrypt编码密码进行匹配。
#3楼
I believe that phrase should have been worded as follows: 我认为这句话应该措辞如下:
bcrypt has salts built into the generated hashes to prevent rainbow table attacks. bcrypt 在生成的哈希中内置了盐,以防止彩虹表攻击。
The bcrypt
utility itself does not appear to maintain a list of salts. bcrypt
实用程序本身似乎没有维护盐列表。 Rather, salts are generated randomly and appended to the output of the function so that they are remembered later on (according to the Java implementation of bcrypt
). 相反,盐是随机生成的,并附加到函数的输出中,以便稍后记住它们(根据bcrypt
的Java实现 )。 Put another way, the "hash" generated by bcrypt
is not just the hash. 换句话说, bcrypt
生成的“哈希” 不仅仅是哈希。 Rather, it is the hash and the salt concatenated. 相反,它是哈希和盐连接。
#4楼
This is bcrypt: 这是bcrypt:
Generate a random salt. 生成随机盐。 A "cost" factor has been pre-configured. 已经预先配置了“成本”因素。 Collect a password. 收集密码。
Derive an encryption key from the password using the salt and cost factor. 使用salt和cost因子从密码派生加密密钥。 Use it to encrypt a well-known string. 用它来加密一个众所周知的字符串。 Store the cost, salt, and cipher text. 存储成本, 盐和密文。 Because these three elements have a known length, it's easy to concatenate them and store them in a single field, yet be able to split them apart later. 因为这三个元素具有已知的长度,所以很容易将它们连接起来并将它们存储在单个字段中,但是稍后可以将它们分开。
When someone tries to authenticate, retrieve the stored cost and salt. 当有人尝试进行身份验证时,检索存储的成本和盐。 Derive a key from the input password, cost and salt. 从输入密码,成本和盐中获取密钥。 Encrypt the same well-known string. 加密相同的知名字符串。 If the generated cipher text matches the stored cipher text, the password is a match. 如果生成的密文与存储的密文匹配,则密码匹配。
Bcrypt operates in a very similar manner to more traditional schemes based on algorithms like PBKDF2. Bcrypt以与基于诸如PBKDF2的算法的更传统方案非常类似的方式操作。 The main difference is its use of a derived key to encrypt known plain text; 主要区别在于使用派生密钥加密已知的纯文本; other schemes (reasonably) assume the key derivation function is irreversible, and store the derived key directly. 其他方案(合理地)假设密钥派生函数是不可逆的,并直接存储派生密钥。
Stored in the database, a bcrypt
"hash" might look something like this: 存储在数据库中的bcrypt
“hash”看起来像这样:
$2a$10$vI8aWBnW3fID.ZQ4/zo1G.q1lRps.9cGLcZEiGDMVr5yUP1KUOYTa $ 2A $ 10 $ vI8aWBnW3fID.ZQ4 / zo1G.q1lRps.9cGLcZEiGDMVr5yUP1KUOYTa
This is actually three fields, delimited by "$": 这实际上是三个字段,由“$”分隔:
-
2a
identifies thebcrypt
algorithm version that was used.2a
标识了所使用的bcrypt
算法版本。 -
10
is the cost factor;10
是成本因素; 2 10 iterations of the key derivation function are used (which is not enough, by the way. I'd recommend a cost of 12 or more.) 2使用密钥派生函数的10次迭代(顺便说一下这是不够的。我建议成本为12或更多。) -
vI8aWBnW3fID.ZQ4/zo1G.q1lRps.9cGLcZEiGDMVr5yUP1KUOYTa
is the salt and the cipher text, concatenated and encoded in a modified Base-64.vI8aWBnW3fID.ZQ4/zo1G.q1lRps.9cGLcZEiGDMVr5yUP1KUOYTa
是salt和密文,在修改后的Base-64中连接和编码。 The first 22 characters decode to a 16-byte value for the salt. 前22个字符解码为盐的16字节值。 The remaining characters are cipher text to be compared for authentication. 其余字符是要进行身份验证的密文。
This example is taken from the documentation for Coda Hale's ruby implementation. 这个例子来自Coda Hale的ruby实现的文档。