参考 - 密码验证

47

经常有人提出问题(尤其是带有标签的问题),询问验证密码的方法。用户通常寻求密码验证方法,这些方法包括确保密码包含特定字符、匹配特定模式和/或符合最低字符数。本文旨在帮助用户找到适当的密码验证方法,而不会大大降低安全性。

因此,问题是:如何正确地验证密码?

1个回答

100

Dilbert comic strip for Mordac, the preventer of information services

为什么密码验证规则很糟糕?

我们的 Jeff Atwood(Coding Horror 博客作者,Stack Overflow 和 Stack Exchange 的联合创始人)于 2017 年 3 月写了一篇关于密码规则的博客,题为 Password Rules are Bullshit。如果您还没有阅读过此文章,我建议您阅读一下,因为它与本文的意图非常相似。

如果您从未听说过 NIST(国家标准与技术研究院),那么您可能没有为项目使用正确的网络安全方法。在这种情况下,请查看他们的 数字身份指南。您还应该保持最佳实践的网络安全知识NIST Special Publication 800-63B (Revision 3) 中提到了以下有关密码规则的内容:

验证器不应强制实施其他组成规则(例如要求混合使用不同的字符类型或禁止连续重复字符)来记忆的秘密。

甚至 Mozilla 的 表单数据验证 文档也取笑于密码规则(页面存档在此):

“你的密码需要在8到30个字符之间,并包含一个大写字母、一个符号和一个数字”(说真的吗?)

如果您为密码设置组合规则会发生什么?您将限制潜在密码数量并删除不符合规则的密码排列。这使得黑客能够确保他们的攻击也是如此!"但是有像千万亿(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个密码)。

xkcd This StackExchange Security post扩展了上面的XKCD漫画。

如何验证密码?

1. 不要自己创建认证

停止要求密码,让人们使用Google、Facebook、Twitter、Yahoo或者其他你信任的有效的互联网驾照来登录。最好的密码是你不需要存储的密码

来源:Jeff Atwood的你的密码太短了

2. 创建你自己的认证

如果您确实必须创建自己的身份验证方法,至少要遵循经过验证的网络安全方法。以下两个部分(2.1和2.2)摘自当前NIST出版物,第5.1.1.2节记忆化秘密验证器

2.1 遵循经过验证的网络安全方法

NIST指出,您应该

  • 要求订阅者选择的记忆密码至少为8个字符。
    • Jeff Atwood建议普通用户的密码最少为10个字符,而具有更高权限(即管理员和版主)的用户的密码最少为15个字符。
  • 允许订阅者选择的记忆密码长度达到64个或更多。
    • 理想情况下,您甚至不应该设置上限。
  • 允许使用所有打印ASCII(包括空格字符)和Unicode。
    • 为了长度要求,每个Unicode代码点将被视为单个字符。
  • 将潜在的密码与包含已知常用、预期或已泄露值的列表进行比较。例如:
    • 从以前的泄漏语料库中获取的密码。
    • 字典词汇。
    • 重复或连续字符(例如aaaaaa1234abcd
    • 特定于上下文的单词,例如服务名称、用户名及其衍生词。
  • 向订阅者提供指导,例如密码强度计。
  • 实施速率限制机制,有效限制可以对订阅者帐户进行的失败身份验证尝试的数量(请参见速率限制(节流))。
  • 如果存在身份验证器被攻击的证据,则强制更改。
  • 允许申请人在输入记忆密码时使用粘贴功能(有助于使用密码管理器,这通常会增加用户选择更强记忆密码的可能性)。

2.2. 不要使用本节中的任何方法!

同一篇文章还指出,您不应该

  • 截断密码。
  • 允许订阅者存储对未经身份验证的申请人可访问的提示
  • 提示订阅者在选择记忆密码时使用特定类型的信息(例如,“你的第一个宠物的名字是什么?”)。
  • 强制实施其他组成规则(例如,要求混合不同字符类型或禁止连续重复字符)以用于记忆密码。
  • 强制要求更改记忆密码(例如,定期更改)。

有大量网站介绍如何创建“正确”的密码验证表单:其中大多数已过时,不应使用。

3. 使用密码熵

在继续阅读本节之前,请注意本节的目的不是为了提供您实施自己的安全方案所需的工具,而是为了向您提供有关当前安全方法如何验证密码的信息。如果您正在考虑创建自己的安全方案,您应该三思而后行,并从StackExchange的安全社区this article中阅读。

3.1. 密码熵的概述

在最基本的级别上,可以使用以下公式计算密码熵:

E = log2(R^L)

在上述公式中:
  • E 代表密码熵
  • R 是唯一字符集合的字符数
  • L 是密码的字符数

这意味着R^L代表可能密码的数量;或者从熵的角度来看,是耗尽所有可能性所需的尝试次数。

不幸的是,这个公式没有考虑到诸如以下因素:

  • 通用密码:如Password1admin
  • 名称:如JohnMary
  • 常用词:例如在英语中的theI
  • 反转/倒置单词:例如drowssap(密码反向)
  • 字母替换(也称为leet):例如P@$$w0rd

为这些额外的考虑因素添加逻辑是一个巨大的挑战。有关现有软件包的信息,请参见3.2节以将其添加到您的项目中。

3.2.现有的密码熵项目

在撰写本文时,目前已知最好的用于估计密码强度的库是Dropbox的zxcvbn(GitHub上的开源项目)。它已经被改编以支持


错误的做法

我理解,每个人的需求都不同,有时候人们想要用错误的方法做事情。对于符合这一标准的人(或者没有选择,已经向他们的经理呈现了上面的所有内容以及更多内容,但他们拒绝更新他们的方法),至少允许使用Unicode字符。一旦你将密码字符限制为特定字符集(即确保小写ASCII字符存在a-z或指定用户可以或不能输入的字符!@#$%^&*()),那么你只是在自找麻烦!

附言:永远不要相信客户端验证,因为它很容易被禁用。这意味着那些试图使用验证密码的人停止。有关更多信息,请参见JavaScript:客户端与服务器端验证

以下正则表达式模式在许多主要的编程语言中都可以使用,但并非所有编程语言都适用()。请注意,以下正则表达式可能在您的语言(甚至是语言版本)中无法使用,您可能需要使用替代方法(例如:请参见Python regex matching Unicode properties)。有些编程语言甚至有更好的方法来检查这种情况(例如使用Password Validation Plugin for ),而不是重复造轮子。如果使用XRegExp addon或其他Unicode类转换工具(如Javascript + Unicode regexes中所述),则在使用时以下内容是有效的。
如果您需要防止控制字符输入,可以使用模式[^\P{C}\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,}$
  • ^ 断言匹配行首。
  • (?=\P{Ll}*\p{Ll}) 确保至少存在一个小写字母(任何语言的)。
  • (?=\P{Lu}*\p{Lu}) 确保至少存在一个大写字母(任何语言的)。
  • (?=\P{N}*\p{N}) 确保至少存在一个数字字符(任何语言的)。
  • (?=[\p{L}\p{N}]*[^\p{L}\p{N}]) 确保至少存在一个非字母或数字的任何字符(任何语言的)。
  • [\s\S]{8,} 匹配任意字符 8 次或更多次。
  • $ 断言匹配行尾。

请谨慎使用以上正则表达式。已经警告过了!


我可以在Swift语言中使用正则表达式“^(?=\P{Ll}*\p{Ll})(?=\P{Lu}*\p{Lu})(?=\P{N}\p{N})(?=[\p{L}\p{N}][^\p{L}\p{N}])[\s\S]{8,}$”吗?我想检查密码是否包含大写字母、小写字母、数字和特殊字符的组合。 - Chandan Jee
@ChandanJee NSRegularExpression 允许使用 Unicode 属性 \p{}\P{},并且还允许使用前瞻 (?=),如文档所述。因此,在 Swift 中可以使用它。但是,我建议您将其实现在后端,因为任何前端代码都可以被绕过(除非数据仅存在于用户设备上)。 - ctwheels

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接