经常有人提出问题(尤其是带有正则表达式标签的问题),询问验证密码的方法。用户通常寻求密码验证方法,这些方法包括确保密码包含特定字符、匹配特定模式和/或符合最低字符数。本文旨在帮助用户找到适当的密码验证方法,而不会大大降低安全性。
因此,问题是:如何正确地验证密码?
经常有人提出问题(尤其是带有正则表达式标签的问题),询问验证密码的方法。用户通常寻求密码验证方法,这些方法包括确保密码包含特定字符、匹配特定模式和/或符合最低字符数。本文旨在帮助用户找到适当的密码验证方法,而不会大大降低安全性。
因此,问题是:如何正确地验证密码?
我们的 Jeff Atwood(Coding Horror 博客作者,Stack Overflow 和 Stack Exchange 的联合创始人)于 2017 年 3 月写了一篇关于密码规则的博客,题为 Password Rules are Bullshit。如果您还没有阅读过此文章,我建议您阅读一下,因为它与本文的意图非常相似。
如果您从未听说过 NIST(国家标准与技术研究院),那么您可能没有为项目使用正确的网络安全方法。在这种情况下,请查看他们的 数字身份指南。您还应该保持最佳实践的网络安全知识。NIST Special Publication 800-63B (Revision 3) 中提到了以下有关密码规则的内容:
验证器不应强制实施其他组成规则(例如要求混合使用不同的字符类型或禁止连续重复字符)来记忆的秘密。
甚至 Mozilla 的 表单数据验证 文档也取笑于密码规则(页面存档在此):
如果您为密码设置组合规则会发生什么?您将限制潜在密码数量并删除不符合规则的密码排列。这使得黑客能够确保他们的攻击也是如此!"但是有像千万亿(1,000,000,000,000,000 or 1x1015)个密码排列": 25-GPU cluster cracks every standard Windows password in <6 hours (958 = 6,634,204,312,890,625 ~ 6.6x1015个密码)。“你的密码需要在8到30个字符之间,并包含一个大写字母、一个符号和一个数字”(说真的吗?)
This StackExchange Security post扩展了上面的XKCD漫画。
停止要求密码,让人们使用Google、Facebook、Twitter、Yahoo或者其他你信任的有效的互联网驾照来登录。最好的密码是你不需要存储的密码。
来源:Jeff Atwood的你的密码太短了。
如果您确实必须创建自己的身份验证方法,至少要遵循经过验证的网络安全方法。以下两个部分(2.1和2.2)摘自当前NIST出版物,第5.1.1.2节记忆化秘密验证器。
NIST指出,您应该:
aaaaaa
,1234abcd
)同一篇文章还指出,您不应该:
有大量网站介绍如何创建“正确”的密码验证表单:其中大多数已过时,不应使用。
在继续阅读本节之前,请注意本节的目的不是为了提供您实施自己的安全方案所需的工具,而是为了向您提供有关当前安全方法如何验证密码的信息。如果您正在考虑创建自己的安全方案,您应该三思而后行,并从StackExchange的安全社区this article中阅读。
在最基本的级别上,可以使用以下公式计算密码熵:
在上述公式中:这意味着代表可能密码的数量;或者从熵的角度来看,是耗尽所有可能性所需的尝试次数。
不幸的是,这个公式没有考虑到诸如以下因素:
Password1
,admin
John
,Mary
the
,I
drowssap
(密码反向)P@$$w0rd
为这些额外的考虑因素添加逻辑是一个巨大的挑战。有关现有软件包的信息,请参见3.2节以将其添加到您的项目中。
在撰写本文时,目前已知最好的用于估计密码强度的库是Dropbox的zxcvbn(GitHub上的开源项目)。它已经被改编以支持.netangularjscc#c++gojavajavascriptobjective-cocamlphppythonrestrubyrustscala。
我理解,每个人的需求都不同,有时候人们想要用错误的方法做事情。对于符合这一标准的人(或者没有选择,已经向他们的经理呈现了上面的所有内容以及更多内容,但他们拒绝更新他们的方法),至少允许使用Unicode字符。一旦你将密码字符限制为特定字符集(即确保小写ASCII字符存在a-z
或指定用户可以或不能输入的字符!@#$%^&*()
),那么你只是在自找麻烦!
附言:永远不要相信客户端验证,因为它很容易被禁用。这意味着那些试图使用javascript验证密码的人停止。有关更多信息,请参见JavaScript:客户端与服务器端验证。
以下正则表达式模式在许多主要的编程语言中都可以使用,但并非所有编程语言都适用(java.netphpperlruby)。请注意,以下正则表达式可能在您的语言(甚至是语言版本)中无法使用,您可能需要使用替代方法(例如python:请参见Python regex matching Unicode properties)。有些编程语言甚至有更好的方法来检查这种情况(例如使用Password Validation Plugin for mysql),而不是重复造轮子。如果使用XRegExp addon或其他Unicode类转换工具(如Javascript + Unicode regexes中所述),则在使用node.js时以下内容是有效的。[^\P{C}\s]
在正则表达式匹配时提示用户。这将仅匹配不是空格字符的控制字符,即水平制表符、换行符、垂直制表符。^(?=\P{Ll}*\p{Ll})(?=\P{Lu}*\p{Lu})(?=\P{N}*\p{N})(?=[\p{L}\p{N}]*[^\p{L}\p{N}])[\s\S]{8,}$
^
断言匹配行首。(?=\P{Ll}*\p{Ll})
确保至少存在一个小写字母(任何语言的)。(?=\P{Lu}*\p{Lu})
确保至少存在一个大写字母(任何语言的)。(?=\P{N}*\p{N})
确保至少存在一个数字字符(任何语言的)。(?=[\p{L}\p{N}]*[^\p{L}\p{N}])
确保至少存在一个非字母或数字的任何字符(任何语言的)。[\s\S]{8,}
匹配任意字符 8 次或更多次。$
断言匹配行尾。请谨慎使用以上正则表达式。已经警告过了!
NSRegularExpression
允许使用 Unicode 属性\p{}
和\P{}
,并且还允许使用前瞻(?=)
,如文档所述。因此,在 Swift 中可以使用它。但是,我建议您将其实现在后端,因为任何前端代码都可以被绕过(除非数据仅存在于用户设备上)。 - ctwheels