我在我的项目中使用 vaadin、spring 和 jpa。我需要检查并通知用户他的密码有多强,并希望在纯 Java 中进行。
你能推荐我最好的方法吗?如果密码至少有一个数字等,是使用特殊库还是只通过正则表达式检查更好。你怎么看?
如果您有任何指向此以及好的库或教程的链接,请发给我。
一般来说,某人密码的强度取决于它的可猜测性。更复杂的密码不易被猜到。您可以通过将其与字典攻击或蛮力算法猜测密码的可能性联系起来来估计复杂性。一旦验证密码不是通用密码,您可以通过使用蛮力猜测它需要多长时间来估计其复杂性。从这个意义上说,你可以测量一个类似的随机密码会有多少二进制熵。这将是对其可猜测性的粗略估计,尽管正如一位评论者指出的那样,测量复杂性是困难的。在我参加的一门安全课程中,讲师建议应该以至少 40 位熵为目标,但 60 位被认为是安全的(假设无法将密码与随机选择的密码区分开来)。所以,密码计的工作是猜测等效随机密码的熵。显然,这里有很多陷阱,使用已建立的库可以为您节省大量时间。
每个字符的熵是 log10(number_possible_characters)/log10(2),因此您可以将熵计算为pwd.length() * Math.log10(numPossibleCharacters)/Math.log10(2)
-- 并且使用 26 个字符,即每个字符大约 4.7 位熵。
以纯科学的方式测量熵并不总是对可猜测性的有效估计,因为密码“abc123”可能看起来和“j3s9fn”一样好(对于计算机,而不是人类),但人类可以很容易地看到第一个比第二个更容易被猜到。严格来说,熵是衡量真正随机性的指标;虽然我们知道人类不是随机的,但它们可能足够接近随机,我们测量熵作为替代,并希望我们的算法测量人类产生的随机性/创造力/不可猜测性的种类。因此,这既是一个技术问题,也是一个人类行为问题。
如果我没记错的话,我曾经被告知英语散文(一页上的文字)每个字符只有大约 1.5 位熵——换句话说,它更容易预测,所以你需要大约 3 次作为如果您只输入英文单词,那么它的密码要“强”,而不是输入随机的小写乱码。坦率地说,我认为需要符号/数字/大写的密码系统是愚蠢的,原因是:如果我愿意,我应该能够输入更多的散文。YMMV。
我们应该通过估计用户可以为他们键入的每个字符选择或可能选择的字符数来开始测量熵。
可能的字符数numPossibleCharacters
取决于用户似乎使用的字符集,或者您允许他们使用的字符集。例如,如果用户键入,abc
那么您会假设他们仅从 26 个可能的字符(每个字符 4.7 个)中进行选择。但是,如果他们键入aBc
您会假设他们从 52(小写和大写)(每个字符 5.7)中选择。如果他们使用数字,添加另外 10 个可能的字符(除非他们似乎只选择明显的数字)。
此外,事后添加数字的用户倾向于将它们放在密码的开头或结尾。因此,他们更改字符集的次数也可能是衡量密码强度的一个很好的指标。例如,“word908”显然不如“w39or7d”安全。当你这样做时,你就超出了对复杂性的简单估计。
如果你使用这个更宽松的复杂性定义,那么很难衡量密码被任何类型的攻击猜到的可能性,尽管你可能会尝试想象一种智能攻击,它选择从最容易猜到和最容易猜到的密码开始的半随机密码。最可能的模式。你可能会说每个字符增加的复杂性(熵?)取决于它是否与前一个字符在同一个字符集中,它是否与前一个字符连续,或者它是否重复一些明显的模式(如“123”或“ABC”)。
您可能会说,从一组字符到另一组字符的每次切换(小写到大写,或数字到符号)本身就是一个随机事件。假设我们定义了 5 个字符集:小写、大写、数字、common_symbols 和 uncommon_symbols。我们检测这些集合中有多少正在使用(例如,如果用户键入“123abc”,charSetsUsed
则为 2)。然后,我们一次循环遍历字符串中的字符。每次用户更改字符集时,我们都会这么说entropy += log(charSetsUsed)/log(2)
。然后,对于每个字符,我们还添加entropy += log(charsInThisCharSet)/log(2)
. [编辑:这不是真正的熵,所以也许您应该将其视为估计的复杂性]
如果你真的想获得技术,你可以测量字符集更改的数量。假设密码长度为 10 个字符。它可以有 1 到 9 组更改,即 10 个选项。然后我们说它们是作为插槽的组合分布的。所以我们这样做:
log(numChanges)/log(2) + log(combination(totalSlots, usedSlots))/log(2).
假设用户输入 aoq35esm42。我们看到他们从一个字符集切换到另一个字符集的 3 个地方。这是一个 10 个字符的密码,因此有 9 个可能的位置用于设置更改(位置出现在两个字符中的任何一个之间),它们的顺序无关紧要(因此组合/二项式系数/n 选择 r):
log(numChanges)/log(2) + log(combination(9, 3))/log(2).
log(3 )/log(2) + log( 84 )/log(2).
1.5849625 + 6.3
7.97727992
所以我们看到我们有将近 8 位的熵,基本上说“在他们可以选择更改字符集的所有地点和时间中,他们可以在这里做的事情大约有 8 个随机位或 2^8 种可能性”。如果我们然后根据每个字符的子集计算每个字符的熵,以及每次更改要使用的子集的选择,那么我们可能会像这样添加熵(如果我在数学上犯了错误,请纠正我):
使用 apache commons MathUtils.binomialCoefficientDouble() 函数计算组合。
此外,如果密码包含字典单词,则认为密码很弱。因此,如果您可以根据字典进行扫描,您可能会假设(猜测)这些字符应该以每个字符 1.5 位熵的英文散文来衡量。但是,您不想将密码暴露给数据库查询(可能会记录它),所以最好的办法是猜测是否有英文单词(祝你好运),比如假设(相当糟糕)任何一组3-5个单格字母字符,包含一个元音,是一个英文单词。或者,您可以构建一个最常见的英语音节字典,这可能更容易存储在内存中。或者,您可以放弃并假设您的用户无论如何都会对系统进行游戏,以使他们的密码更容易记住。
尽管如此,如果您确定要准确,您可以将英语单词的内存数据库存储在安全内存中。
所有这些加起来是一个相当复杂的算法,它可能仍然不完整。您可能应该进行相对简单的计算,或者使用其他人已经编写的库。
''安全存储''
计算密码安全性的方法并没有真正解决一个完全不同的问题:在密码通过网络或客户端内存时保持密码安全。我不是这里的专家,但我已经阅读了足够多的书,知道要保持警惕并做好功课。至少,我会使用 https 并强烈考虑使用浏览器的本机密码输入字段。这是避免使用自己的技术,而是使用图书馆的另一个原因(只要你能说他们做的功课比你能应付的多;我不知道我会相信技术,除非我愿意调查了它)。此外,您永远不应以明文形式存储密码,而应使用单向加密/散列。对你的密码加盐,并使用安全的散列算法(不是 MD5;也许更喜欢 SHA2?)对它们进行散列,可能多次重新散列和重新加盐(以增加字典攻击的成本)。通常有一些库会为您处理这类事情,即使它们不测量密码强度;我知道 C# 或 .NET 有专门针对加盐、散列和迭代的库;等等。可能还有其他问题我在这里没有提到——请记住密码强度只是安全链中的一个环节。
在纯 java 中,您可以使用VT Password来检查密码强度。可从 Maven 中心获得:
<dependencies>
<dependency>
<groupId>edu.vt.middleware</groupId>
<artifactId>vt-password</artifactId>
<version>3.1.1</version>
</dependency>
<dependencies>