ale*_*lex 2 java cryptography spring-security pbkdf2 spring-boot
encode当多次运行spring security Pbkdf2PasswordEncoder 实例的方法时,该方法对于相同的输入返回不同的结果。片段
String salt = "salt";
int iterations = 100000;
int hashWidth = 128;
String clearTextPassword = "secret_password";
Pbkdf2PasswordEncoder pbkdf2PasswordEncoder = new Pbkdf2PasswordEncoder(salt, iterations, hashWidth);
String derivedKey = pbkdf2PasswordEncoder.encode(clearTextPassword);
System.out.println("derivedKey: " + derivedKey);
String derivedKey2 = pbkdf2PasswordEncoder.encode(clearTextPassword);
System.out.println("derivedKey2: " + derivedKey2);
Run Code Online (Sandbox Code Playgroud)
结果是这样的输出
derivedKey: b6eb7098ee52cbc4c99c4316be0343873575ed4fa4445144
derivedKey2: 2bef620cc0392f9a5064c0d07d182ca826b6c2b83ac648dc
Run Code Online (Sandbox Code Playgroud)
两个推导的预期输出将是相同的值。此外,当再次运行该应用程序时,输出将再次不同。对于具有相同输入的两个不同 Pbkdf2PasswordEncoder 实例,也会出现不同的输出行为。该encoding方法的行为更像是随机数生成器。使用的 Spring boot 版本是 2.6.1 , spring-security-core 版本是 5.6.0 。
我是否缺少任何明显的设置?该文档没有给出额外的提示。spring boot项目设置是否存在概念性错误?
我是否缺少任何明显的设置?
是的。您链接到的文档相当清楚,我猜您错过了。Pbkdf2PasswordEncoder您传递给构造函数的字符串不是盐!
编码器会为您生成一个盐,并且每次您要求它编码某些内容时都会生成一个盐,这就是您应该如何做这些事情1。(返回的字符串在单个字符串中包含随机生成的盐以及应用编码的结果)。由于每次调用时都会生成一个新的 salt .encode,因此.encode每次调用都会返回不同的值,即使您使用相同的输入来调用它也是如此。
您传入的字符串仅仅是“另一个秘密” - 有时可能很有用(例如,如果您可以将此秘密存储在安全飞地中,或者它是由另一个系统发送/在启动时输入并且从未存储在磁盘上,那么如果有人偷走了你的服务器,他们就无法检查密码。PBKDF 意味着,如果他们确实拥有秘密,则检查速度会非常慢,但如果他们没有,他们甚至无法启动)。
这似乎是一个可靠的计划——否则人们就会开始做愚蠢的事情。例如使用字符串"salt"作为所有编码的盐:)
真正的问题是:
两个推导的预期输出值相同
不,你的期望被打破了。无论您编写的任何代码做出了这种假设,都需要扔掉。例如,这就是您打算如何使用编码器:
当用户创建新密码时,您可以使用.encode此方法返回的内容并将其存储在数据库中。
当用户登录时,您将获取他们输入的内容,然后从数据库(发送给您的数据库)中获取字符串.encode并调用.matches.
听起来您想再次运行.encode并查看是否匹配。不是您应该如何使用此代码。
您还需要检查您的安全策略。你脑子里关于这个东西如何工作的想法已经彻底被打破了。想象一下它按照您想要的方式工作,并且所有密码编码都使用一个盐。然后,如果你给我你的数据库转储,我可以在大约 10 分钟内轻松破解所有帐户的大约 5%!
如何?好吧,我对所有散列字符串进行排序,然后计算出现次数。里面会出现一堆重复的字符串。然后,我可以获取其密码位于最常见哈希值前 10 名的所有用户,然后以他们的身份登录。因为他们的密码是iloveyou、welcome123、princess、dragon、12345678、alexsawesomeservice!、 等等 - 通常是非常常用的密码。我怎么知道那是他们的密码?因为他们的密码与您系统上许多其他用户的密码相同。
此外,如果通用密码都不起作用,我可以看出这些很可能是来自同一用户的不同帐户。
这些都是我绝对不应该从原始数据中得出的东西。当然,解决方案是为所有内容提供唯一的盐,然后将盐与哈希值一起存储在数据库中,以便在用户尝试登录时可以“重建”。这些工具试图让您的生活变得轻松为您做工作。这是一个好主意,因为安全实现中的错误(例如忘记加盐,或为所有用户使用相同的盐)不能(轻松)进行单元测试,所以一个善意的开发人员编写代码,它似乎可以工作,一个随意的看一眼密码散列似乎表明“它正在工作”(散列对于肉眼来说似乎足够随机),然后它被部署,安全问题等等。
| 归档时间: |
|
| 查看次数: |
1894 次 |
| 最近记录: |