基于表单的网站身份验证的权威指南

5518

版主注意:

根据目前适用于Stack Overflow的相关性规则,本问题不适合我们的问答格式。对于仍具有价值的内容,我们通常使用“历史锁定”。但是,此问题的答案正在积极维护,而历史锁定不允许编辑答案。因此,已应用“维基答案”锁定以允许编辑答案。您应该假设通常由历史锁定处理的相关问题存在(即,此问题不是Stack Overflow上一个好的主题问题的好例子)。

网站的基于表单的身份验证

我们认为Stack Overflow不仅应该是解决特定技术问题的资源,还应提供解决常见问题变体的通用指南。"网站的基于表单的身份验证"应该是这样一项实验的好话题。

它应包括以下主题:

  • 如何登录
  • 如何退出登录
  • 如何保持登录状态
  • 管理Cookie(包括推荐设置)
  • SSL/HTTPS加密
  • 如何存储密码
  • 使用密保问题
  • 忘记用户名/密码功能
  • 使用nonces防止跨站请求伪造(CSRF)
  • OpenID
  • "记住我"复选框
  • 浏览器自动填充用户名和密码
  • 秘密URL(公共URL由摘要保护)
  • 检查密码强度
  • 电子邮件验证
  • 以及更多关于基于表单的身份验证的内容...

不包括以下内容:

  • 角色和授权
  • HTTP基本身份验证

请通过以下方式帮助我们:

  1. 建议子主题
  2. 提交有关此主题的良好文章
  3. 编辑官方答案

54
为什么要排除HTTP基本身份验证?它可以通过Ajax在HTML表单中工作:http://www.peej.co.uk/articles/http-auth-with-html-forms.html - system PAUSE
56
HTTP基本认证具有一个特性,即相对难以使浏览器忘记。如果您不使用SSL来保护连接(即HTTPS),它也非常不安全。 - Donal Fellows
24
我认为有必要谈论一下会话(包括固定和劫持)、Cookie(安全性和HttpOnly标志)以及基于HTTP的SSO。 - symcbean
29
HttpOnly 是一个非常有用的 cookie 标记,它可以防止基于 JavaScript 的 cookie 盗取,这是 XSS 攻击的一个子集。建议在某处提到此标记。 - Alan H.
81
哇,回答很冗长,有些回答甚至获得了几十个赞,但是没有人提到一种常见错误,就是通过HTTP传输登录表单。我甚至曾与人争论过这个问题,他们说:“但它提交到https://…”,然后当我问他们是否确定攻击者没有篡改表单所在的非加密页面时,他们只是茫然不知。 - dzuelke
显示剩余11条评论
11个回答

3959

第一部分:如何登录

我们假设您已经知道如何构建一个登录+密码的HTML表单,将值POST到服务器端的脚本进行身份验证。下面的部分将涉及实用的身份验证模式以及如何避免最常见的安全陷阱。

使用HTTPS还是不使用HTTPS?

除非连接已经安全(即通过SSL/TLS隧道传输的HTTPS),否则您的登录表单值将以明文形式发送,这允许任何窃听浏览器和Web服务器之间的线路的人在传递时读取登录信息。政府通常会进行此类窃听,但总的来说,我们不会处理其他所有权线路,只需说一句:请使用HTTPS。

实质上,保护登录过程免受窃听/数据包嗅探的唯一实用方法是使用HTTPS或其他基于证书的加密方案(例如TLS)或经过验证的挑战-响应方案(例如基于Diffie-Hellman的SRP)。任何其他方法都可以轻松地被窃听攻击者绕过

当然,如果您愿意有点不切实际,您也可以采用某种形式的双因素认证方案(例如Google Authenticator应用程序、物理的“冷战风格”代码簿或RSA密钥生成器dongle)。如果正确应用,即使使用不安全的连接,这也可以起作用,但很难想象开发人员愿意实施双因素身份验证而不是SSL。 (不要)自己编写JavaScript加密/哈希 考虑到在网站上设置SSL证书的感知成本(尽管现在可避免)和技术难度,一些开发人员会尝试自己编写浏览器中的哈希或加密方案,以避免通过不安全的信道传递明文登录信息。
虽然这是一个高尚的想法,但除非它与上述方案之一相结合,否则它基本上是无用的(并且可能存在安全漏洞),即使用强加密保护线路或使用经过验证的挑战-响应机制(如果您不知道这是什么,请知道这是数字安全中最难以证明、设计和实施的概念之一)。
尽管对于密码泄露,散列密码可以有效,但它容易受到重放攻击、中间人攻击/劫持(如果攻击者可以在未经安全保护的 HTML 页面到达浏览器之前注入几个字节,他们可以简单地在 JavaScript 中注释掉散列),或暴力攻击(因为您将用户名称、盐和散列密码全部交给攻击者)。 反人类的 CAPTCHA CAPTCHA 旨在防止一种特定类型的攻击:自动字典/暴力试错攻击,没有人工操作员。毫无疑问,这是一个真正的威胁,然而,有一些处理方式可以无缝地解决这个问题,而不需要使用 CAPTCHA,具体来说是正确设计的服务器端登录限制方案-我们稍后会讨论这些。
要知道,CAPTCHA 实现并不相同;它们通常不能被人类解决,大多数实际上对机器人无效,所有实现都无法对廉价的第三世界劳动力产生影响(根据 OWASP,当前的血汗工厂率为每 500 次测试 12 美元),而且某些实现在某些国家可能是技术上非法的(请参见 OWASP 认证防伪指南)。如果必须使用 CAPTCHA,请使用 Google 的 reCAPTCHA,因为它根据定义是 OCR-hard(因为它使用已经 OCR-misclassified 的书籍扫描),并且非常努力地使用户友好。
个人而言,我倾向于认为验证码很烦人,只有在用户多次登录失败且限制延迟已达到最大值时才使用它们作为最后的手段。这种情况很少发生,因此可以接受,并且它会加强整个系统。
存储密码/验证登录
经过近年来广泛报道的黑客攻击和用户数据泄漏事件,这可能已经是常识了,但必须说一下:不要在数据库中以明文形式存储密码。用户数据库经常被黑客攻击、泄漏或通过SQL注入获取,如果您以原始的明文密码进行存储,那么登录安全性立即结束。
因此,如果无法存储密码,如何检查从登录表单POST的登录+密码组合是否正确?答案是使用密钥派生函数进行哈希。每当创建新用户或更改密码时,将密码通过KDF(例如Argon2、bcrypt、scrypt或PBKDF2)运行,将明文密码(“correcthorsebatterystaple”)转换为一个长的、看起来随机的字符串,这样在数据库中存储更安全。要验证登录,您需要对输入的密码再次运行相同的哈希函数,这次传入盐并将生成的哈希字符串与存储在数据库中的值进行比较。Argon2、bcrypt和scrypt已经将盐与哈希一起存储。请查看sec.stackexchange上的文章以获取更详细的信息。
一个盐的原因是单纯的哈希不足以保护哈希值免受彩虹表攻击。盐有效地防止两个完全匹配的密码被存储为相同的哈希值,从而防止在攻击者执行密码猜测攻击时整个数据库一次性被扫描。

密码存储不应使用加密哈希,因为用户选择的密码不够强大(即通常不包含足够的熵),攻击者可以在相对较短的时间内完成密码猜测攻击。这就是为什么要使用KDF的原因 - 这些方法有效地"延长密钥", 这意味着每个攻击者猜测密码都会导致多次重复哈希算法,例如10,000次,从而使攻击者猜测密码变慢10,000倍。

会话数据 - "您已登录为Spiderman69"

一旦服务器已经验证了登录和密码与用户数据库匹配,系统需要一种方式来记住浏览器已经通过身份验证。这个事实只能在会话数据中存储在服务器端。

如果你不熟悉会话数据,这是它的工作原理:一个单一的随机生成的字符串被存储在一个过期的cookie中,并用于引用一个数据集合 - 会话数据 - 这些数据被存储在服务器上。如果你使用的是MVC框架,这无疑已经处理好了。

如果可能的话,在将会发送到浏览器的会话cookie中设置安全和HTTP Only标志。HttpOnly标志可提供一定保护,防止通过XSS攻击读取cookie。安全标志确保仅通过HTTPS发送cookie,从而防止网络嗅探攻击。cookie的值不应是可预测的。如果出现引用不存在的会话的cookie,则应立即替换其值以防止会话固定
客户端还可以维护会话状态。这可以通过使用JWT(JSON Web Token)等技术实现。
第二部分:如何保持登录状态-臭名昭著的“记住我”复选框
长时间登录cookie(“记住我”功能)是一个危险区域;一方面,当用户知道如何处理它们时,它们完全与常规登录一样安全;另一方面,当不小心使用它们的用户在公共计算机上使用它们并忘记注销时,它们是巨大的安全风险,并且他们可能不知道浏览器cookie是什么或如何删除它们。
个人而言,我喜欢为我经常访问的网站使用长时间登录,但我知道如何安全地处理它们。如果您确定您的用户也知道相同的内容,则可以放心使用长时间登录。如果没有 - 那么您可能认同这样的理念:如果用户对其登录凭据不小心,他们就会自食其果。我们也不会去用户家里撕掉那些密码写在显示器边缘上的让人面红耳赤的便笺。
当然,有些系统无法承受任何账户被黑客攻击的风险;对于这样的系统,你无法为持久登录提供合理的解释。
如果您决定实现持久登录 cookie,则需要按照以下步骤操作:
1.首先,花些时间阅读Paragon Initiative's article。您需要正确处理很多元素,该文章很好地解释了每个元素。
2.并且要再次强调最常见的一个问题,不要将持久登录 cookie(令牌)存储在数据库中,只需存储其哈希值!登录令牌等同于密码,因此如果攻击者得到了您的数据库,他们可以使用令牌登录任何帐户,就像明文登录密码组合一样。因此,在存储持久性登录令牌时,请使用哈希(根据https://security.stackexchange.com/a/63438/5002,弱哈希就足够了)。
第三部分:使用密保问题 不要实现“密保问题”。 “密保问题”功能是安全反模式。阅读MUST-READ列表中的链接4的论文。你可以问问萨拉·佩林(Sarah Palin),她的雅虎邮件帐户在之前的总统竞选中被黑客攻击,因为她的安全问题的答案是...“Wasilla High School”!
即使用户指定问题,大多数用户选择以下两个选项的可能性非常高:
  • 一个“标准”的秘密问题,如母亲的娘家姓或最喜欢的宠物

  • 一个简单的琐事,任何人都可以从他们的博客、LinkedIn资料或类似的地方获得

  • 任何比猜测他们的密码更容易回答的问题。对于任何合理的密码来说,你能想到的每一个问题都是这样

总之,在几乎所有形式和变化中,安全问题本质上都是不安全的,不应出于任何原因在身份验证方案中使用。

安全问题为什么会存在于公共领域?真正的原因是它们方便地节省了一些支持电话的费用,以及无法访问其电子邮件以获取重新激活代码的用户。这是以安全和萨拉·佩林的声誉为代价的。值得吗?可能不值得。

第四部分:忘记密码功能

我已经提到过为什么您永远不应该使用安全问题来处理忘记/丢失的用户密码;同时也不用说您永远不应该通过电子邮件发送用户的实际密码。在这个领域,还有至少两个常见的陷阱需要避免:

  1. 不要将忘记的密码重置为自动生成的强密码 - 这些密码非常难记,这意味着用户必须更改密码或将其写下来 - 例如在显示器边缘的明黄色便笺上。相反,只需让用户立即选择新密码 - 这正是他们想做的。 (唯一的例外可能是如果用户普遍使用密码管理器来存储/管理密码,否则很难记住这些密码)。
  2. 始终对数据库中的丢失密码代码/令牌进行哈希处理。 再次强调,此代码是另一个密码等效项的示例,因此必须进行哈希处理,以防攻击者接触到您的数据库。当请求丢失密码代码时,请将明文代码发送到用户的电子邮件地址,然后对其进行哈希处理,在数据库中保存哈希值 - 并丢弃原始代码。就像密码或持久登录令牌一样。

最后注意:始终确保输入“丢失密码代码”的界面至少与登录表单本身一样安全,否则攻击者将简单地使用该界面获得访问权限。确保生成非常长的“丢失密码代码”(例如16个区分大小写的字母数字字符)是一个好的开始,但考虑为登录表单本身添加相同的限制方案。

第五部分:检查密码强度

首先,您需要阅读这篇小文章进行现实检查:500个最常见的密码

好的,所以这个列表可能不是任何系统中最常见密码的规范列表,但它很好地说明了当没有强制策略时人们选择密码的糟糕程度。此外,与公开可用的最近被盗密码分析相比,该列表看起来非常接近家庭。
因此:如果没有最低密码强度要求,则有2%的用户使用前20个最常见的密码之一。意思是:如果攻击者只尝试20次,您网站上50个帐户中将有1个易受攻击。
为了防止这种情况,需要计算密码的熵,然后应用阈值。国家标准与技术研究院(NIST)Special Publication 800-63提出了一套非常好的建议。结合字典和键盘布局分析(例如,“qwertyuiop”是一个糟糕的密码),可以在18位熵水平上拒绝99%的所有选择不当的密码。仅仅计算密码强度并向用户显示可视化强度计是好的,但不足够。除非得到强制执行,否则许多用户很可能会忽略它。

我建议阅读Randall Munroe的Password Strength xkcd,以了解高熵密码的用户友好性。

使用Troy Hunt的Have I Been Pwned API检查用户密码是否已被泄露。

第六部分:更多内容 - 或者:防止快速登录尝试

首先,看一下这些数字:Password Recovery Speeds - How long will your password stand up

如果你没有时间查看该链接中的表格,请看下面的列表:

  1. 即使使用算盘,破解一个弱密码也需要几乎没有时间

  2. 如果密码不区分大小写,那么破解一个由字母和数字组成的9位密码几乎没有时间

  3. 如果密码长度少于8个字符,即使是由符号、字母和数字等复杂的大小写混合密码,也只需要几乎没有时间(桌面PC可以在数天甚至数小时内搜索整个7个字符的密钥空间)

  4. 但是,如果你每秒只能尝试一次,即使是6位密码,破解它需要很长时间!

那么我们从这些数字中可以学到什么呢?很多,但我们可以关注最重要的部分:防止大量快速连续的登录尝试(即暴力破解攻击)实际上并不那么困难。但是,要正确地防止它并不像看起来那么容易。

一般来说,你有三种选择,都可以有效地防止暴力攻击(以及字典攻击,但由于你已经采用了强密码策略,它们不应该是问题)

  • 在N次失败尝试后呈现CAPTCHA(非常烦人且通常无效 - 但我在这里重复了)

  • 锁定帐户并在N次失败尝试后要求电子邮件验证(这是一个等待发生DoS攻击)

  • 最后,登录节流:即,在N次失败尝试后设置时间延迟(是的,DoS攻击仍然可能发生,但至少它们不太可能,并且更加复杂)。

最佳实践#1:短暂延迟随着失败尝试次数而增加,例如:

  • 1次失败尝试=无延迟
  • 2次失败尝试= 2秒延迟
  • 3次失败尝试= 4秒延迟
  • 4次失败尝试= 8秒延迟
  • 5次失败尝试= 16秒延迟
  • 等。

对此进行DoS攻击将非常不切实际,因为结果锁定时间略大于先前锁定时间之和。

澄清一下:延迟不是在将响应返回给浏览器之前的延迟。它更像是一个超时或冷却期,在此期间,特定帐户或特定IP地址的登录尝试将根本不被接受或评估。也就是说,正确的凭据不会返回成功的登录,而不正确的凭据也不会触发延迟增加。

最佳实践 #2: 在N次失败尝试后执行中等长度的时间延迟,例如:

  • 1-4次失败尝试=无延迟
  • 5次失败尝试=15-30分钟延迟

对这种方案进行DoS攻击将非常不切实际,但确实可行。此外,需要注意的是,这样长时间的延迟可能会让合法用户感到非常恼火。健忘的用户会讨厌你。

最佳实践 #3: 结合两种方法 - 一种是在N次失败尝试后执行固定的短时间延迟,例如:

  • 1-4次失败尝试=无延迟
  • 5次或更多失败尝试=20秒延迟

或者,采用逐步增加的延迟和固定的上限,例如:

  • 1次失败尝试=5秒延迟
  • 2次失败尝试=15秒延迟
  • 3次或更多失败尝试=45秒延迟

这个最终方案来自于OWASP最佳实践建议(MUST-READ清单中的链接1),即使它确实有一定的限制性,也应该被视为最佳实践。

然而,作为一个经验法则,我会说:你的密码策略越强,你就越不需要用延迟来干扰用户。如果你要求强密码(区分大小写的字母数字+必须包含数字和符号)长度为9个或更多字符,你可以在激活节流之前给用户2-4次无延迟的密码尝试。

这种最终的登录限制方案很难遭到DoS攻击。为了保证合法用户不会受到影响,始终允许持久性(cookie)登录(和/或CAPTCHA验证的登录表单)通过,即使攻击正在进行中。这样,非常不切实际的DoS攻击变成了极其不切实际的攻击。
此外,对于管理员帐户,执行更积极的限制是有意义的,因为这些是最有吸引力的入口点。
除此之外,更高级的攻击者将尝试通过“分散他们的活动”来规避登录限制:
- 将尝试分布在僵尸网络上以防止IP地址标记 - 与其选择一个用户并尝试50,000个最常见的密码(由于我们的限制,他们无法这样做),他们将选择最常见的密码并针对50,000个用户进行尝试。这样,他们不仅可以规避像CAPTCHA和登录限制这样的最大尝试措施,而且他们的成功率也会提高,因为第一位最常见的密码比第49,995位更有可能。 - 为每个用户帐户间隔30秒的登录请求,以逃过雷达
在这里,最佳实践是记录全系统失败的登录次数,并使用站点坏登录频率的运行平均值作为您对所有用户强制执行的上限的基础。

太抽象了?让我来重新表述一下:

假设您的网站在过去3个月中平均每天有120次错误登录。使用该值(运行平均值),您的系统可能将全局限制设置为3倍,即24小时内失败尝试次数达到360次。然后,如果所有帐户的失败尝试总数在一天内超过该数字(或者更好的是,监视加速率并触发计算阈值),它将激活系统范围的登录限制 - 这意味着所有用户都会有短暂延迟(仍然除了cookie登录和/或备用CAPTCHA登录)。

我还发布了一个问题,其中更多细节和如何避免棘手陷阱的真正好的讨论,以抵御分布式暴力攻击

第八部分:双因素身份验证和认证提供商

凭据可能会被攻击者获取,无论是通过漏洞、密码被写下并丢失、带有密钥的笔记本电脑被盗或用户输入登录信息到网络钓鱼网站中。登录可以通过双因素身份验证进一步保护,该身份验证使用带外因素,如从电话呼叫、短信、应用程序或加密狗接收的一次性代码。几家提供双因素身份验证服务的公司。

认证可以完全委托给单点登录服务,其中另一个提供商处理收集凭据的问题。这将问题推向了可信赖的第三方。Google和Twitter都提供基于标准的SSO服务,而Facebook提供了类似的专有解决方案。

关于Web身份验证的必读链接

  1. OWASP身份验证指南 / OWASP身份验证备忘单
  2. Web客户端身份验证的Dos and Don'ts(非常易读的MIT研究论文)
  3. 维基百科:HTTP cookie
  4. 回退身份验证的个人知识问题:Facebook时代的安全问题(非常易读的伯克利研究论文)

71
我不完全同意验证码的部分,是的,验证码很烦人,而且它们可以被破解(除了reCAPTCHA,但这几乎无法被人类解决!)但这就像说不要使用垃圾邮件过滤器,因为它的误判率低于0.1%..这个网站也使用验证码,它们并不完美,但可以防止大量的垃圾邮件,目前还没有更好的替代品。 - Waleed Eissa
257
@Jeff: 很抱歉听到你对我的回复有意见。我不知道Meta上有关于这个答案的辩论,如果你让我知道的话,我会很乐意自己进行编辑。而删除我的帖子导致了我账户中失去了1200点声望值,这让我感到很痛心。:( - Jens Roland
14
发送认证令牌后,系统需要一种方式来记住您已经通过验证 - 这个事实应该只在会话数据的服务器端存储。可以使用Cookie引用会话数据。不完全正确。您可以(并且为无状态服务器应该)使用加密签名的Cookie。这是不可能被伪造的,不会占用服务器资源,也不需要粘性会话或其他花招。 - Martin Probst
12
一台台式电脑可以在不到90天的时间内搜索完最多包含7个字符的所有密钥。而一台配置良好的GPU机器则可以在不到1天的时间内搜索完同样长度的密钥空间。顶级的GPU机器能够每秒处理10亿个哈希值。这些结论对于密码存储方面有一些间接的启示,但并未直接涉及该问题。 - Frank Farmer
6
“@MikeMike: "..and loop through them in php" -- 为什么不直接在 SQL 中选择行?SELECT * FROM LoginTokens WHERE UserID=[cookie 中的用户 ID] AND HashedToken=[cookie 中的 token 的哈希值] 应该可以正常工作(但请记得对 SQL 使用准备语句 / 存储过程)。" - Jens Roland
显示剩余65条评论

435

定冠词

发送凭证

100%安全地发送凭证的唯一实用方法是使用SSL。 使用JavaScript对密码进行哈希处理是不安全的。 客户端密码哈希的常见陷阱:

  • 如果客户端和服务器之间的连接未加密,则您所做的一切都容易受到中间人攻击 的攻击。 攻击者可以替换传入的javascript以破坏哈希,或将所有凭据发送到其服务器,他们可以侦听客户机响应并完美地冒充用户等。 SSL与受信任的证书颁发机构一起设计,旨在防止中间人攻击。
  • 如果您在服务器上没有进行其他冗余工作,则接收服务器的哈希密码不够安全

还有另一种安全的方法称为 SRP ,但它是受专利保护的(尽管它是自由授权),且可用的好实现很少。

存储密码

不要将密码以明文形式存储在数据库中,即使您不关心自己网站的安全性也是如此。假设您的一些用户会重复使用其在线银行账户的密码。因此,请存储已散列的密码并丢弃原始密码。同时确保密码不会出现在访问日志或应用程序日志中。OWASP 推荐在新应用程序中首选使用Argon2。如果不可用,则应改用PBKDF2或scrypt。最后,如果以上都不可用,请使用bcrypt。
仅使用哈希也不安全。例如,相同的密码意味着相同的哈希,这使得哈希查找表成为一种有效的破解多个密码的方法。相反,请存储加盐哈希。盐是附加到密码之前进行哈希处理的字符串-每个用户使用不同(随机)的盐。盐是公共值,因此可以将它们与哈希一起存储在数据库中。有关更多信息,请参见此处
这意味着您无法向用户发送他们忘记的密码(因为您只有哈希)。除非您已经验证了用户(用户必须证明他们能够阅读发送到存储的(并验证的)电子邮件地址的电子邮件),否则请勿重置用户的密码。

安全问题

安全问题不安全-避免使用它们。为什么?任何安全问题所做的事情,密码都可以更好地完成。在这里阅读@Jens Roland answer中的第三部分:使用秘密问题
会话cookie 用户登录后,服务器会向用户发送会话cookie。服务器可以从cookie中检索用户名或ID,但其他人无法生成这样的cookie(TODO解释机制)。 Cookie可以被劫持:它们只有客户端计算机和其他通信一样安全。它们可以从磁盘中读取,在网络流量中嗅探,被跨站点脚本攻击提取,从被污染的DNS中钓鱼以便客户端将其cookie发送到错误的服务器。不要发送持久性cookie。Cookie应在客户端会话结束时过期(浏览器关闭或离开您的域)。
如果您想自动登录用户,则可以设置一个持久性cookie,但它应与完整会话cookie不同。您可以设置一个附加标志,表示用户已自动登录,并且需要真正登录以进行敏感操作。这在希望为您提供无缝个性化购物体验但仍保护您的财务详细信息的购物网站上很受欢迎。例如,当您返回访问亚马逊时,他们会向您显示一个看起来像已登录的页面,但当您去下订单(或更改您的送货地址、信用卡等)时,他们会要求您确认密码。

金融网站(如银行和信用卡)只有敏感数据,不应允许自动登录或低安全模式。

外部资源列表


1
鉴于最近围绕签名 SSL 证书的 MITM 漏洞(https://blog.startcom.org/?p=145),因此 SSL 和某种挑战-响应身份验证的组合(SRP 的替代方案)可能是一个更好的解决方案。 - Kevin Loney
很多这些东西都是情境相关的。我倾向于根本不使用会话Cookie。Cookie被劫持几乎总是服务器的问题。中间人攻击/数据包嗅探并不常见。 - Shawn
BCrypt Nuget包:http://nuget.org/List/Packages/BCrypt - Fabian Vilers
1
关于此答案的注释1:这是一个草稿,需要作为维基进行编辑。如果您可以编辑它,欢迎您加入。 - Peter Mortensen
如果我理解正确,SRP是针对存在多个参与方的情况而言的。 - Webwoman

170

首先,强烈声明这个答案并不是最适合这个问题的。它绝对不应该成为最佳答案!

我会提及Mozilla提出的BrowserID(或更准确地说,Verified Email Protocol),以期在未来找到更好的身份验证方法的升级路径。

我将总结如下:

  1. Mozilla是一家非营利组织,其价值观与找到解决此问题的好方案非常契合。
  2. 事实上,今天大多数网站都使用基于表单的身份验证。
  3. 基于表单的身份验证有一个很大的缺陷,即增加了网络钓鱼的风险。用户被要求在一个由远程实体控制的区域中输入敏感信息,而不是由其用户代理(浏览器)控制的区域。
  4. 由于浏览器是隐式信任的(用户代理的整个概念就是代表用户行事),它们可以帮助改善这种情况。
  5. 阻碍进展的主要力量是部署僵局。解决方案必须分解为提供一些独立好处的步骤。
  6. 表达内置于互联网基础设施中的身份的最简单的去中心化方法是域名。
  7. 作为表达身份的第二级别,每个域管理其自己的账户集合。
  8. 格式“账户@域名”简洁明了,并且受到广泛的协议和URI方案的支持。这样的标识符当然最普遍被认为是电子邮件地址。
  9. 电子邮件提供商已经成为在线的事实上主要身份提供者。当前的密码重置流程通常允许您控制一个帐户,如果您可以证明自己控制该帐户关联的电子邮件地址。
  10. 验证电子邮件协议旨在提供一种基于公钥加密的安全方法,以简化向域B证明您在域A拥有帐户的过程。
  11. 对于不支持验证电子邮件协议的浏览器(当前所有浏览器),Mozilla提供了一个shim,它在客户端JavaScript代码中实现了该协议。
  12. 对于不支持验证电子邮件协议的电子邮件服务,该协议允许第三方充当受信任的中间人,声称他们已验证用户拥有帐户。不希望有大量这样的第三方;此功能仅旨在允许升级路径,并且更喜欢电子邮件服务自己提供这些声明。
  13. Mozilla提供了自己的服务来充当这样的受信任第三方。实施验证电子邮件协议的服务提供商(即,依赖方)可以选择相信Mozilla的声明或不相信。Mozilla的服务使用发送确认链接的传统方式验证用户的帐户所有权。
  14. 服务提供商当然可以将该协议作为选项提供,以补充他们可能希望提供的任何其他身份验证方法。
  15. 正在寻求的一个重要用户界面优势是“身份选择器”。当用户访问网站并选择进行身份验证时,他们的浏览器会向他们显示一组电子邮件地址(“个人”、“工作”、“政治活动”等),他们可以用这些地址来识别自己对该网站。
  16. 作为这一努力的一部分,正在寻求的另一个重要用户界面优势是帮助浏览器更多地了解用户的会话 - 他们当前以谁的身份登录,主要是为了
    这并不是严格意义上的“基于表单的网站认证”。但这是一种努力,旨在从当前基于表单的认证规范过渡到更安全的浏览器支持的认证方式。

3
BrowserID链接已失效。 - Spoody
该项目似乎已经被搁置...请参见https://en.wikipedia.org/wiki/Mozilla_Persona。 - Jeff Olson

152

我想分享一下我发现的解决方案,它非常有效。

我称其为“虚假字段”(虽然我并不是发明者,所以不要归功于我)。其他人称之为蜜罐。

简而言之:你只需要在你的<form>中插入这个字段,并在验证时检查它是否为空:

<input type="text" name="email" style="display:none" />
诀窍在于欺骗机器人以为它必须向一个必填字段插入数据,这就是我将输入项命名为“电子邮件”的原因。如果您已经有一个名为电子邮件的字段并正在使用它,则应该尝试将虚拟字段命名为其他名称,例如“公司”,“电话”或“电子邮件地址”。只需选择您知道不需要的某些内容,并且听起来像人们通常会在Web表单中填写的逻辑内容。现在使用CSS或JavaScript / jQuery将input字段隐藏-无论哪种方法最合适-只要不要将输入type设置为hidden ,否则机器人将不会上当。

在验证表单(客户端或服务器端)时,请检查您的虚拟字段是否已填写,以确定它是由人还是机器人发送的。

示例:

对于人类:用户将看不到虚拟字段(在我的情况下命名为“电子邮件”),并且不会尝试填写它。因此,当表单发送后,虚拟字段的值仍应为空。

对于机器人:机器人将看到类型为text的字段和名称为email(或您所称呼的任何内容),并且将逻辑地尝试使用适当的数据填充它。它不在乎您是否使用一些花哨的CSS来设计输入表单,Web开发人员经常这样做。无论虚拟字段中的值是什么,只要它大于0个字符即可。

我将此方法与CAPTCHA结合在留言簿上使用,并且从那以后就没有看到过一篇垃圾邮件。我之前使用过仅CAPTCHA的解决方案,但最终导致每小时约有五篇垃圾邮件。在表单中添加虚拟字段已经阻止了所有垃圾邮件的出现(至少到目前为止)。

我相信这也可以很好地用于登录/身份验证表单。

警告:当然,这种方法并非100%可靠。机器人可以被编程忽略应用了display:none样式的输入字段。您还必须考虑使用某些自动完成形式(如大多数浏览器都内置的!)为他们自动填充所有表单字段的人。他们可能会选择一个虚拟字段。

您还可以通过将虚拟字段保持可见但位于屏幕范围之外来稍微改变它,但这完全取决于您。

要有创意!


37
这是一个有用的反垃圾邮件技巧,但我建议使用一个除了“email”以外的字段名称,否则你可能会发现浏览器的自动填充功能会把它填满,无意中阻止了你网站的真实用户。 - Nico Burns
8
我还有几个元素使用了visibility:hiddenposition:absolute;top:-9000px,你还可以在其中的一些元素上使用text-indentz-index属性,将它们放在带有奇怪名称的压缩CSS文件中。因为机器人可以检测到display:none,现在他们会检查一系列的组合方式。我实际上使用这些方法,它们是老行业的技巧。+1 - TheBlackBenzKid
21
如果一个视障用户使用屏幕阅读器导航表单,会发生什么? - soycharliente
9
这项技术有一个名称:蜜罐(Honeypot)http://zh.wikipedia.org/wiki/蜜罐_(计算机安全) - pixeline
27
无需内联样式,只需为字段添加一个类(可以使用一些怪异的词汇,这些词汇很难被机器人理解),并通过网站的CSS文件进行隐藏。例如:<input type="text" name="email" class="cucaracha">,然后在CSS中写入:.cucaracha { display:none; } - Ricardo Zea
显示剩余11条评论

88
我认为上面的回答并不是“错误”的,但是有许多身份验证领域没有涉及(或者更确切地说,重点在于“如何实现cookie会话”,而不是“有哪些选项可用以及它们之间的权衡”)。
我的建议编辑/答案如下:
- 问题更多地出现在账户设置上而非密码检查上。 - 使用双因素身份验证比使用更聪明的密码加密方式更安全。 - 不要尝试自己实现登录表单或密码数据库存储,除非存储的数据在账户创建时毫无价值且是自动生成的(即像Facebook、Flickr等Web 2.0风格)。 - 摘要认证是一种基于标准的方法,得到了所有主流浏览器和服务器的支持,甚至不会在安全通道上发送密码。 这样就避免了需要“会话”或cookie的任何需要,因为浏览器本身每次都会重新加密通信。这是最“轻量级”的开发方法。
然而,我不建议这样做,除非是针对公共、低价值的服务。这也是上面其他答案中存在的问题——不要尝试重新实现服务器端身份验证机制——这个问题已经被解决,并且被大多数主流浏览器支持。不要使用cookie。不要在自己编写的数据库中存储任何东西。只需按请求询问请求是否经过身份验证。其他所有内容都应该由配置和第三方受信任的软件支持。
所以...
首先,我们将账户的初始创建(包括密码)与随后重新检查密码混淆了。如果我是Flickr并且第一次为您创建网站,则新用户可以访问零价值(空白网络空间)。我真的不在乎创建帐户的人是否在撒谎。如果我正在创建医院内部网络/外部网络的帐户,则价值在于所有医疗记录,因此我确实关心帐户创建者的身份(*)。
这是非常困难的部分。唯一合理的解决方案是建立信任网络。例如,您作为医生加入医院。您在某个地方创建了一个网页,其中包含您的照片、护照号码和公钥,并使用私钥对它们进行了哈希。然后,您访问医院,系统管理员查看您的护照,看看照片是否与您相符,然后使用医院私钥哈希网页/照片哈希。从现在开始,我们可以安全地交换密钥和令牌。任何信任医院的人也可以这样做(这是秘密酱料)。系统管理员还可以给您提供RSA加密狗或其他双因素身份验证。
但是,这是很麻烦的,而且不太符合Web 2.0的要求。但是,这是创建具有访问非自行创建信息的新帐户的唯一安全方式。
  1. Kerberos和SPNEGO - 带有可信第三方的单点登录机制 - 基本上用户会对可信第三方进行验证。(注意这与不可信的OAuth没有任何关系)

  2. SRP - 一种聪明的密码认证方式,无需可信第三方。但是在这里,我们进入了“即使成本更高也更安全使用双重身份验证”的领域。

  3. SSL客户端 - 给客户端提供公钥证书(支持所有主要浏览器 - 但会引发关于客户端机器安全性的问题)。

最终,这是一个权衡 - 安全漏洞的成本与实施更安全方法的成本之间的权衡。总有一天,我们可能会看到一个广泛接受的适当的PKI,因此不再需要自己编写认证表单和数据库。总有一天...


30
很难确定你所指的答案是哪一个,当你说“我不认为上面的答案是‘错误的’”时。 - Davorak

60

当进行哈希处理时,不要使用快速的哈希算法,如MD5(存在许多硬件实现)。请使用类似SHA-512的算法。对于密码而言,慢一些的哈希算法更好。

您能够越快地创建哈希值,黑客使用暴力破解检查器所需的时间就越短。因此,较慢的哈希算法将减缓暴力破解速度。慢一些的哈希算法将使暴力破解长密码(8个字符或更多)变得不切实际。


6
SHA-512同样运行速度很快,因此需要进行数千次迭代。 - Seun Osewa
7
更像是像bcrypt这样被设计成慢速哈希的东西。 - Fabian Nicollier
5
正如另一个回答中提到的,"OWASP 建议在新应用程序中首选使用 Argon2。如果不可用,则应改用 PBKDF2 或 scrypt。最后,如果以上都不可用,则使用 bcrypt。" 无论何时都不应该使用 MD5 或 SHA 哈希函数来进行密码哈希。这个答案给出了错误的建议。 - Mike

57

更好的选择——难以记忆,难以猜测:密码管理器......链接到2005年的一篇文章,其中可能指的是Excel电子表格 :) - felickz

33

我想提供一个基于防御深度的建议。你不需要为管理员和普通用户使用相同的认证系统。你可以在单独的URL上,使用单独的代码执行请求以授予高权限的登录表单。这个表单可以做出对普通用户来说非常麻烦的决策。其中一个我使用过的技巧是实际上将管理员访问的登录URL进行混淆,并将新的URL发送给管理员。这样一来,任何暴力攻击都会马上停止,因为你的新URL可以任意复杂(非常长的随机字符串),但管理员用户唯一需要做的就是按照邮件中的链接进行操作。攻击者再也不知道该POST到哪里了。


一封电子邮件中的简单链接实际上并不安全,因为电子邮件本身就不安全。 - David Spector
它和其他基于令牌的密码重置系统一样安全,但不是双因素认证。而几乎所有的密码重置系统都不支持双因素认证。 - Iain Duncan

22

我不知道选择回答还是评论更好,但我选择了第一种选项。

关于第一个回答中的 PART IV:忘记密码功能,我想提到时序攻击(Timing Attacks)。

记住密码 的表单中,攻击者可能会检查完整的电子邮件列表,并检测哪些电子邮件已注册到系统中(请参见下面的链接)。

关于忘记密码表单,建议设置一些延迟函数,使成功和不成功的查询之间具有相等的时间间隔。

https://crypto.stanford.edu/~dabo/papers/webtiming.pdf


20

我想要补充一个非常重要的评论:

  • 在"企业内部网络设置"中,大多数情况下,以上提到的内容可能并不适用!

许多公司部署了"仅供内部使用"的网站,它们实际上是通过URL实现的"企业应用程序"。这些URL只能在"公司内部网络"中解析。(神奇地包括所有VPN连接的'路上战士')

当用户连接到上述网络时,他们的身份("认证")已经被[确定...],以及他们的权限("授权")去做某些事情......如访问此网站。

这个"认证+授权"服务可以由多种不同的技术提供,例如LDAP(Microsoft OpenDirectory)或Kerberos。

从你的角度来看,你只需要知道:任何合法到达你网站的人必须携带一个"标记"(缺少这样的标记必须立即退回404 Not Found)。

标记的价值对你没有意义,但如果需要,你的网站可以"[权威地]询问知情人(LDAP等)"关于你可能有的任何和每一个问题。换句话说,你不利用任何"自行开发的逻辑"。相反,你询问权威并隐含地信任其裁决。

嗯......这是从"狂野和暴力的互联网"中进行的相当的思维转变。


9
你小时候有没有掉进标点符号井里? :) 我已经读了三遍,但还是不明白你要表达什么。但如果你的意思是“有时候不需要基于表单的身份验证”,那么你是正确的。但考虑到我们正在讨论何时需要它,我不明白为什么这很重要要注意呢? - Hugo Delsing
2
我的观点是,公司之外的世界与公司内部完全不同。如果您正在构建一个可由公众访问的应用程序,并且面向“广泛的网络”,那么您别无选择,只能自己编写身份验证和授权方法。但是,在公司内部,唯一进入的方式是在公司内部或使用VPN,那么很可能该应用程序将不会有 - 必须没有 - “自己”的方法来执行这些操作。应用程序必须使用这些方法,以提供一致的、集中管理的服务。 - Mike Robinson
2
即使是内部网络,建筑物也需要最低限度的安全保障。销售部门有机密的盈利和损失数字,而工程部门则有机密的知识产权。许多公司在部门或分部之间限制数据流动。 - Sablefoste

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