参考 - 密码验证

Reference - Password Validation

提问人: 提问时间:1/20/2018 最后编辑:ctwheels 更新时间:6/23/2021 访问量:20203

问:

很多时候,问题(尤其是那些标记正式的问题)会询问验证密码的方法。用户似乎通常会寻求密码验证方法,包括确保密码包含特定字符、匹配特定模式和/或遵守最小字符数。这篇文章旨在帮助用户在不大大降低安全性的情况下找到适当的密码验证方法。

所以问题是:应该如何正确验证密码?

正则表达式 验证 身份验证 密码 .net angularjs c++ go java javascript objective-c ocaml php python rest ruby rust scala perl mysql node.js

评论


答:

100赞 19 revs, 3 users 88%ctwheels #1

Dilbert comic strip for Mordac, the preventer of information services

为什么密码验证规则不好?

我们自己的 Jeff Atwood(Coding Horror 的博主,Stack Overflow 和 Stack Exchange 的联合创始人)早在 2017 年 3 月就写了一篇关于密码规则的博客,标题为“密码规则是胡说八道”。如果你还没有读过这篇文章,我强烈建议你这样做,因为它极大地反映了这篇文章的意图。

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

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

甚至Mozilla关于表单数据验证的文档也嘲笑了密码规则(页面存档在这里):

“您的密码长度需要在 8 到 30 个字符之间,并包含一个大写字母、一个符号和一个数字”(认真的?

如果对密码施加组合规则,会发生什么情况?您限制了潜在密码的数量,并删除了与您的规则不匹配的密码排列。这使黑客可以确保他们的攻击也这样做!“是的,但有千万亿(1,000,000,000,000,000,000 或 1x1015)密码排列”:25-GPU 集群在 <6 小时内破解每个标准 Windows 密码(958 = 6,634,204,312,890,625 ~ 6.6x1015 个密码)。

xkcd 这篇 StackExchange Security 文章扩展了上面的 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 安全社区的这篇文章

3.1. 密码熵概述

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

E = log2(R^L)

在上面的公式中:

  • E表示密码熵
  • R是唯一字符中的字符数
  • L是密码中的字符数

这意味着这R^L表示可能的密码数量;或者,就熵而言,用尽所有可能性所需的尝试次数。

不幸的是,这个公式没有考虑的是以下情况:

  • 通用密码:即 ,Password1admin
  • 名称: i.e. ,JohnMary
  • 常用词: i.e. In the English language ,theI
  • 反转/反转词:即 (密码倒置)drowssap
  • 字母替换(又名 leet):即P@$$w0rd

为这些额外的注意事项添加逻辑是一个巨大的挑战。请参阅 3.2,了解可以添加到项目的现有包。

3.2. 现有的密码熵项目

在撰写本文时,用于估计密码强度的现有最著名的库是 Dropbox(GitHub 上的一个开源项目)的 zxcvbn。它已被调整为支持 c c#


以错误的方式做事

然而,我理解每个人都有不同的要求,有时人们想以错误的方式做事。对于那些符合此标准的人(或者别无选择,并且已经向您的经理提供了本节以上的所有内容以及更多内容,但他们拒绝更新他们的方法),至少允许 Unicode 字符。当您将密码字符限制为一组特定的字符(即确保存在小写 ASCII 字符或指定用户可以或不能输入的字符)的那一刻,您只是在自找麻烦!a-z!@#$%^&*()

附言永远不要相信客户端验证,因为它很容易被禁用。这意味着对于那些尝试使用 STOP 验证密码的人来说。有关详细信息,请参阅 JavaScript:客户端与服务器端验证

以下正则表达式模式并非在所有编程语言中都有效,但在许多主要编程语言()中都有效。请注意,以下正则表达式可能不适用于您的语言(甚至语言版本),您可能需要使用替代方法(即 :请参阅匹配 Unicode 属性的 Python 正则表达式)。一些编程语言甚至有更好的方法来检查这种事情(即使用密码验证插件),而不是重新发明轮子。使用 ,如果使用 XRegExp 插件或其他一些 Unicode 类转换工具,如 Javascript + Unicode 正则表达式中所述,以下内容是有效的。

如果需要阻止输入控制字符,可以使用模式 [^\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 次或更多次。
  • $断言行尾的位置。

请自行决定使用上述正则表达式。你已经被警告了!

评论

0赞 Chandan Jee 7/17/2020
我可以在 swift 语言中使用正则表达式 ^(?=\P{Ll}*\p{Ll})(?=\P{Lu}*\p{Lu})(?=\P{N}*\p{N})(?=[\p{L}\p{N}]*[^\p{L}\p{N}])[\s\S]{8,}$ 吗?我想使用大写、小写、数字和特殊字符组合检查密码
0赞 ctwheels 7/17/2020
@ChandanJee确实允许 Unicode 属性,并且还允许文档中所述的展望。所以是的,这将在 Swift 中工作。但是,我建议您在后端实现这一点,因为可以绕过任何前端代码(当然,除非数据仅存在于用户的设备上)。NSRegularExpression\p{}\P{}(?=)