每当您需要在应用程序中实现密码哈希或散列时,您应该牢记一些最佳实践。永远不要自己实现密码哈希算法改用经过严格审查的开发人员库!密码学是一个复杂的领域,如果你尝试自己实现一个流行的算法,就会出现很多问题。随着计算机每年变得越来越强大,密码哈希算法(及其参数)需要调整!现代密码哈希算法依赖于您(作为开发人员)来指定他们在计算哈希时应该使用多少资源,并且随着时间的推移,您将需要更新这些参数。掌握此类情况的唯一方法是紧跟最新的安全新闻和趋势。 在决定如何存储敏感密码时,你应该考虑的第一件事是你是否想自己处理认证和授权问题。 有许多服务可以为你解决这个问题。使用认证和授权服务可以消除你在应用程序中安全存储密码的负担。 有许多商业服务,如Auth0和Okta,使之变得简单。只要你选择的供应商支持OpenIDConnect等开放标准,你就能轻松地将它们集成到你的Java应用中(例如SpringBoot)。 此外,你的供应商可能也能支持MFA(多因素认证),以进一步提高你的终端用户的安全性。 如果您确实需要自己在Java应用程序中存储密码,那么本文适合您。 如果您在应用程序中存储用户名和密码,无论您做什么,都不要以纯文本形式存储密码 密码哈希算法可将纯文本密码转换为一串数据,您可以安全地存储在数据库中,并且永远不会被反转(您可以将纯文本密码转换为哈希,但不可能将哈希转换为密码,因此命名为单向函数)。 密码哈希算法是专为处理密码而设计的单向函数。 您需要一个强大的密码散列算法来处理您的用户密码。一个好的密码散列算法会消耗大量的计算能力和内存。在谈论密码哈希算法时:他们使用的计算资源越多越好! Argon2id 目前(2022年)使用的最佳算法是Argon2id。Argon2是一个密钥推导函数,被选为密码哈希竞赛的获胜者。它有三个不同的版本:Argon2d:最大限度地抵抗GPU破解攻击Argon2i:针对侧信道攻击进行了优化Argon2id:混合版本 建议使用Argon2id版本的密码,因为它可以平衡侧通道和基于GPU的攻击。您可以通过提供三个参数来配置Argon2id算法:内存大小(m)、迭代次数(t)和并行度(p)。 目前,建议使用Argon2id,内存至少为15MiB,迭代次数为2,并行度为1。该算法已经包括为每个用户自动加盐以防止预先计算的攻击,所以这里不需要做任何特别的事情。 将SpringSecurityCrypto与BouncyCastle一起使用 在Java应用程序中实现Arong2id的最简单方法是使用springsecuritycrypto库。尽管它是Spring框架的一部分,但您不必使用Spring框架的其余部分。 springsecuritycrypto库有一个Argon2PasswordEncoder您可以使用的。我个人认为命名有点偏离,因为这在技术上不是编码器而是哈希器。 Spring库bouncycastle用作包含Argon2算法的基于Java的完整实现的依赖项。 springsecuritycrypto库可以被认为是一个直观的界面bouncycastle,使其更易于使用。dependencygroupIdorg。springframework。securitygroupIdspringsecuritycryptoartifactIdversion5。6。2versiondependencydependencygroupIdcommonslogginggroupIdcommonsloggingartifactIdversion1。2versiondependencydependencygroupIdorg。bouncycastlegroupIdbcpkixjdk15onartifactIdversion1。70versiondependency 默认情况下Argon2PasswordEncoder使用Argon2id版本,m4MiB、t3和p1。尽管迭代次数高于最小值,但您可能仍要考虑更高的内存消耗。 在下面的Java示例中,我使用了m15Mib、t2和p1的最低要求。我使用与盐和哈希长度的默认值相同的值。Argon2PasswordEncoderencoderbnewbArgon2PasswordEncoder(32,64,1,151024,2);bvarbmyPasswordfontThisIsMyPbvarbencodedPasswordencoder。encode(myPassword);System。out。println(encodedPassword);bvarbvalidPasswordencoder。matches(myPassword,encodedPassword);System。out。println(validPassword);font 代码的输出:argon2idv19m15360,t2,p1YpRuuQhW1dHOimAnWD5TRU6SebitufIrmIrenrYOMhkEXhhHpu2NUcPwhV4IUQelQdf4I8ViyFsFiC8BYEisE3oWFv96zYeNA1iawhaDo1XHz6Pp1r55SSI4AIATrue 为JVM使用Argon2绑定 JVM库的Argon2Binding是Spring库的替代方案。该库由MoritzHalbritter创建,使用JNA调用原生C库。这种方法的优点是它使用了Argon2作者的原始实现,并且可能包括性能优势。因此,您需要能够访问系统上的底层C库。 您可以选择使用包管理器自己安装此C库,或者依赖包含一组预编译Argon2库的库。推荐第一个,原因很明显,它更小。 没有预编译库:dependencygroupIdde。mkammerergroupIdargon2jvmnolibsartifactIdversion2。11versiondependency 使用预编译库:dependencygroupIdde。mkammerergroupIdargon2jvmartifactIdversion2。11versiondependency 这个库的使用非常简单。在下面的代码示例中,您可以看到一个与Spring示例等效的示例,使用相同的最小参数。Argon2argon2Argon2Factory。create(Argon2Factory。Argon2Types。ARGON2id,32,64);bvarbmyPasswordfontThisIsMyPbvarbhashargon2。hash(2,151024,1,myPassword。toCharArray());System。out。println(hash);bvarbvalidPasswordargon2。verify(hash,myPassword。toCharArray());System。out。println(validPassword);font scrypt 如果你因为任何原因不能使用Argon2id,scrypt是一个不错的第二选择。与argon2id类似,scrypt是另一种强大的密码散列算法,它允许你配置各种成本参数来增加它所消耗的资源。 根据OWASPPasswordcheatsheet,,你应该使用一个最小的CPU内存成本参数(N),一个最小的块大小(r),以及一个并行化参数(p)。下面的选项被认为是你应该使用的最小值。N216(64MiB),r8(1024字节),p1N215(32MiB),r8(1024字节),p2N214(16MiB),r8(1024字节),p4N213(8MiB),r8(1024字节),p8N212(4MiB),r8(1024字节),p15 请注意,以上的scrypt参数列表都会产生同样强大的密码哈希值。上面的选项只是展示了通过为N、r和p参数指定不同的值,你可以让scrypt算法使用更多的CPU或内存资源,这取决于你的偏好。我下面的建议展示了真实世界的用法,你可以简单地复制和粘贴,以达到简便的目的。 springsecuritycrypto库支持许多算法,包括SCrypt。你可以按照我们使用Argon2Encoder的方式使用SCryptEncoder。在下面的例子中,我使用了中间的建议,在CPU和内存分配之间提供了一个良好的平衡。SCryptPasswordEncoderencoderbnewbSCryptPasswordEncoder(16384,8,4,32,64);bvarbmyPasswordfontThisIsMyPbvarbencodedPasswordencoder。encode(myPassword);System。out。println(encodedPassword);bvarbvalidPasswordencoder。matches(myPassword,encodedPassword);System。out。println(validPassword);font 输出:e08048FQ4xntwEz2ZNu8QRyIQJlAXRgQkiG3WulLMEqkioVtaFiKE7sZDGgtmqUmwB8OEf7Eagux9QXG478unLwRS1Bz5Uf30dWGxcvtlkjj7tPnPdgq8YD1V8odhPW4Abtrueb 使用BCRYPT 如果Argon2id和scrypt不可用,另一个强有力的选择是BCrypt。如果你决定使用BCrypt,工作系数参数应该设置为不低于10。 你可以以类似于以前的方式使用springsecuritycrypto库。默认的工作系数被设置为10,但我们在下面的例子中把它设置为14(在2022年是一个合理的数字)。BCryptPasswordEncoderencoderbnewbBCryptPasswordEncoder(14)。 你设置的工作系数越高,哈希值就越强,但它也会花费更多的CPU资源(和时间!)来完成运行。在我的本地机器上,设置运行BCrypt的工作负荷为10,平均花费约89ms。对于14的工作量,平均是1033ms。这几乎是12倍的时间。这绝不是一个准确的基准,但它确实显示了增加工作因素的影响。