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

基于表单的网站身份验证

我们认为,Stack Overflow不应该仅仅是解决非常具体的技术问题的资源,还应该是如何解决常见问题变体的一般指南。“基于表单的网站身份验证”应该是这样一个实验的好主题。

它应包括以下主题:

  • 如何登录
  • 如何注销
  • 如何保持登录
  • 管理cookie(包括推荐设置)
  • SSL/HTTPS加密
  • 如何存储密码
  • 使用秘密问题
  • 忘记用户名/密码功能
  • 使用nonces来防止跨站点请求伪造(CSRF)
  • OpenID
  • “记住我”复选框
  • 浏览器自动完成用户名和密码
  • 秘密URL(公共url受摘要保护)
  • 检查密码强度
  • 电子邮件验证
  • 和更多关于基于表单的认证

它不应该包括以下内容:

  • 三、角色与授权
  • HTTP基本认证

请帮助我们:

  1. 建议副标题
  2. 关于这个主题的好文章
  3. 编辑官方答案
661625 次浏览

权威文章

发送凭证

100%安全地发送凭据的唯一实用方法是使用SSL。使用JavaScript散列密码是不安全的。客户端密码散列的常见缺陷:

  • 如果客户端和服务器之间的连接未加密,您所做的一切都是容易受到中间人攻击。攻击者可以替换传入的javascript以破坏散列或将所有凭据发送到他们的服务器,他们可以监听客户端响应并完美地模拟用户等。具有受信任证书颁发机构的SSL旨在防止MitM攻击。
  • 如果您不在服务器上执行额外的冗余工作,则服务器收到的哈希密码为不太安全

还有另一种称为SRP的安全方法,但它已获得专利(尽管它是自由许可),并且几乎没有可用的好实现。

存储密码

不要将密码作为明文存储在数据库中。即使您不关心自己网站的安全性。假设您的一些用户会重复使用他们在线银行帐户的密码。因此,存储哈希密码,并丢弃原始密码。并确保密码不会出现在访问日志或应用程序日志中。OWASP建议使用Argon2作为新应用程序的首选。如果这不可用,应该使用PBKDF2或scrypt。最后,如果上述都不可用,请使用bcrypt。

哈希本身也不安全。例如,相同的密码意味着相同的哈希-这使得哈希查找表成为一次破解大量密码的有效方法。相反,存储盐渍哈希。盐是在哈希之前附加到密码的字符串-每个用户使用不同的(随机)盐。盐是一个公共值,因此您可以将它们与哈希一起存储在数据库中。有关更多信息,请参阅这里

这意味着您不能向用户发送他们忘记的密码(因为您只有哈希)。除非您对用户进行了身份验证,否则不要重置用户的密码(用户必须证明他们能够阅读发送到存储(和验证)电子邮件地址的电子邮件。)

安全问题

安全问题是不安全的-避免使用它们。为什么?安全问题所做的任何事情,密码都更好。在本wiki中阅读@Jens Roland回复中的第三部分:使用秘密问题

会话cookie

用户登录后,服务器向用户发送会话cookie。服务器可以从cookie中检索用户名或id,但其他人无法生成这样的cookie(待办事项说明机制)。

Cookie可能被劫持:它们仅与客户端机器的其余部分和其他通信一样安全。它们可以从磁盘读取,在流量中嗅探,通过反射形的XSS攻击解除,从有毒的DNS中钓鱼,以便客户端将cookie发送到错误的服务器。不要发送持久性cookie。Cookie应在客户端会话结束时过期(浏览器关闭或离开您的域)。

如果您想自动登录您的用户,您可以设置一个持久性cookie,但它应该与完整会话cookie不同。您可以设置一个额外的标志,表明用户已经自动登录,对于敏感操作需要真正登录。这在那些希望为您提供无缝、个性化购物体验但仍保护您的财务详细信息的购物网站中很受欢迎。例如,当您返回访问亚马逊时,他们会向您显示一个看起来像您已经登录的页面,但当您去下订单(或更改送货地址、信用卡等)时,他们会要求您确认密码。

另一方面,银行和信用卡等金融网站只有敏感数据,不应该允许自动登录或低安全模式。

外部资源列表

第一部分:如何登录

我们假设您已经知道如何构建登录+密码超文本标记语言表单,该表单将值POST到服务器端的脚本进行身份验证。下面的部分将讨论声音实用身份验证的模式,以及如何避免最常见的安全隐患。

使用HTTPS还是不使用HTTPS?

除非连接已经安全(即使用SSL/TLS通过HTTPS隧道),否则您的登录表单值将以明文形式发送,这允许任何在浏览器和Web服务器之间的线路上窃听的人都能够在登录通过时读取登录信息。这种类型的窃听通常由政府进行,但一般来说,我们不会解决“拥有”的电线,除了这样说:只需使用HTTPS。

从本质上讲,在登录期间防止窃听/数据包嗅探的唯一方法是使用HTTPS或其他基于证书的加密方案(例如,TLS)或经过验证和测试的挑战-响应方案(例如,基于迪菲-赫尔曼的SRP)。

当然,如果你愿意变得有点不切实际,你也可以使用某种形式的双重验证方案(例如谷歌身份验证器应用程序,物理“冷战风格”密码本,或RSA密钥生成器加密狗)。如果应用得当,这甚至可以在不安全的连接上工作,但很难想象开发人员会愿意实现双因素身份验证而不是SSL。

(不)滚动自己的JavaScript加密/哈希

考虑到在您的网站上设置SSL证书的感知成本(尽管现在为0)和技术难度,一些开发人员试图推出自己的浏览器内哈希或加密方案,以避免通过不安全的线路传递明文登录。

虽然这是一个高尚的想法,但它本质上是无用的(并且可以是安全漏洞),除非它与上述之一相结合-也就是说,要么使用强加密保护线路,要么使用经过验证的挑战-响应机制(如果你不知道那是什么,只要知道它是最难证明的,最难设计的,也是最难实现的数字安全概念之一)。

虽然散列密码可以密码泄露有效,但它很容易受到重放攻击,中间人攻击/劫持(如果攻击者可以在您的不安全超文本标记语言页面到达您的浏览器之前向其注入几个字节,他们可以简单地注释掉JavaScript中的散列),或暴力攻击(因为您同时向攻击者提供用户名,盐和散列密码)。

验证码反人类

验证码旨在阻止一种特定类别的攻击:无人操作的自动字典/暴力试错。毫无疑问,这是一个真正的威胁,然而,有一些无缝处理它的方法不需要验证码,特别是设计得当的服务器端登录节流方案——我们将在后面讨论。

要知道CAPTCHA的实现是不一样的;它们通常不是人工可解决的,它们中的大多数实际上对机器人无效,所有这些对于廉价的第三世界劳动力都是无效的(根据OWASP,目前的血汗工厂费率是每500次测试12美元),并且一些实现在某些国家可能在技术上是非法的(见OWASP认证备忘单)。如果你必须使用CAPTCHA,请使用Google的reCAPTCHA,因为它根据定义是OCR困难的(因为它使用了已经OCR错误分类的书籍扫描)并且非常努力地变得用户友好。

就我个人而言,我倾向于发现验证码很烦人,并且只有在用户多次登录失败并且限制延迟达到最大时才使用它们作为最后的手段。这种情况很少发生,足以让人接受,它加强了整个系统。

存储密码/验证登录

在近年来我们看到的所有高度公开的黑客攻击和用户数据泄露之后,这可能最终成为常识,但必须说:不要在数据库中以明文形式存储密码。用户数据库经常被黑客攻击、泄露或通过SQL注入收集,如果您存储的是原始明文密码,那对您的登录安全性来说就是即时游戏结束。

那么,如果您无法存储密码,如何检查登录表单中POSTed的登录名+密码组合是否正确?答案是使用密钥推导函数进行哈希运算。每当创建新用户或更改密码时,您都会获取密码并通过KDF(例如Argon2、bcrypt、scrypt或PBKDF2)运行它,将明文密码(“纠正密码”)转换为一个长的、随机的字符串,这在您的数据库中存储要安全得多。要验证登录,您需要对输入的密码运行相同的哈希函数,这次传入盐并将生成的哈希字符串与存储在数据库中的值进行比较。Argon2,bcrypt和scrypt已经用哈希存储盐。查看sec.stackexchange上的文章以获取更详细的信息。

使用盐的原因是哈希本身是不够的——您需要添加一个所谓的“盐”来保护哈希免受彩虹桌的影响。盐有效地防止两个完全匹配的密码被存储为相同的哈希值,防止在攻击者执行密码猜测攻击时一次扫描整个数据库。

加密哈希不应该用于密码存储,因为用户选择的密码不够强(即通常不包含足够的熵),并且可以访问哈希的攻击者可以在相对较短的时间内完成密码猜测攻击。这就是为什么使用KDF-这些实际上是“伸展钥匙”,这意味着攻击者猜测的每个密码都会导致哈希算法的多次重复,例如10,000次,这导致攻击者猜测密码的速度减慢10,000倍。

会话数据-“您以Spiderman69身份登录”

一旦服务器根据您的用户数据库验证了登录名和密码并找到匹配项,系统需要一种方法来记住浏览器已通过身份验证。这一事实应该只存储在服务器端的会话数据中。

如果您不熟悉会话数据,它是如何工作的:一个随机生成的字符串存储在即将到期的cookie中,并用于引用存储在服务器上的数据集合-会话数据。如果您使用的是MVC框架,这无疑已经得到处理。

如果可能的话,请确保会话cookie在发送到浏览器时设置了安全和仅HTTP标志。HttpOnly标志为通过XSS攻击读取cookie提供了一些保护。安全标志确保cookie仅通过HTTPS发送回来,因此可以防止网络嗅探攻击。cookie的值不应可预测。如果出现引用不存在会话的cookie,则应立即替换其值以防止会话固定

会话状态也可以在客户端维护。这是通过使用JWT(JSON Web Token)等技术实现的。

第二部分:如何保持登录-臭名昭著的“记住我”复选框

持久性登录Cookie(“记住我”功能)是一个危险区域;一方面,当用户了解如何处理它们时,它们与传统登录完全一样安全;另一方面,它们是粗心用户手中的巨大安全风险,他们可能在公共计算机上使用它们并忘记注销,并且可能不知道什么是浏览器cookie或如何删除它们。

就我个人而言,我喜欢经常访问的网站的持续登录,但我知道如何安全地处理它们。如果你确信你的用户也知道这些,你可以问心无愧地使用持续登录。如果不是——好吧,那么你可以订阅这样的理念:如果用户对登录凭据不小心,如果他们被黑客攻击,这是他们自己造成的。这不像我们去用户的房子,撕掉所有那些带有密码的Facebook便利贴,他们在显示器边缘排列着密码。

当然,有些系统无法承受任何帐户被黑客入侵;对于这样的系统,您无法证明持久登录是合理的。

如果您决定实施持久登录cookie,您可以这样做:

  1. 首先,花点时间阅读关于这个主题的Paragon Initiative的文章。你需要正确处理一系列元素,这篇文章在解释每个元素方面做得很好。

  2. 重申一个最常见的陷阱,请勿将永久登录Cookie(TOKEN)存储在您的数据库中,仅存储一部分!登录令牌是密码等效的,因此如果攻击者接触到您的数据库,他们可以使用令牌登录任何帐户,就像它们是明文登录密码组合一样。因此,在存储持久登录令牌时使用哈希(根据https://security.stackexchange.com/a/63438/5002,弱哈希就可以用于此目的)。

第三部分:使用秘密问题

不要实施“秘密提问”。“秘密问题”功能是一种安全反模式。阅读必读列表中链接号4的论文。你可以向莎拉佩林询问这个问题,因为她的雅虎!电子邮件帐户在前一次总统竞选期间被黑客入侵,因为她的安全问题的答案是…“瓦西拉高中”!

即使是用户指定的问题,大多数用户也很可能会选择:

  • 一个“标准”的秘密问题,如母亲的娘家姓或最喜欢的宠物

  • 一个简单的琐事,任何人都可以从他们的博客,LinkedIn个人资料或类似的东西中提取

  • 任何比猜测密码更容易回答的问题。对于任何像样的密码,这是你能想象到的每一个问题

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

安全问题甚至存在于野外的真正原因是,它们方便地节省了无法访问电子邮件的用户的一些支持电话的费用,以获得重新激活代码。这是以牺牲安全和莎拉佩林的声誉为代价的。值得吗?可能不是。

第四部分:忘记密码功能

我已经提到了为什么你应该永远不要问安全问题处理忘记/丢失的用户密码;不言而喻,你永远不应该通过电子邮件向用户发送他们的实际密码。在这个领域至少有两个更常见的陷阱需要避免:

  1. 不要将忘记的密码改为自动生成的强密码——众所周知,这样的密码很难记住,这意味着用户必须要么更改它,要么把它写下来——比如,写在显示器边缘的亮黄色便利贴上。与其设置新密码,不如让用户马上选择一个新密码——这是他们无论如何都想做的。(如果用户普遍使用密码管理器来存储/管理密码,通常不写下来就无法记住这些密码,可能会是一个例外)。

  2. 始终散列数据库中丢失的密码代码/令牌。再一次,此代码是密码等效的另一个示例,因此必须对其进行散列,以防攻击者进入您的数据库。当请求丢失的密码代码时,将明文代码发送到用户的电子邮件地址,然后散列,将散列保存在您的数据库中-和扔掉原来的。就像密码或持久登录令牌一样。

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

第五部分:检查密码强度

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

好吧,也许这个列表不是任何系统任何地方上最常见密码的规范列表,但它很好地表明,当没有强制政策时,人们会选择多么糟糕的密码。另外,当你将它与最近被盗密码的公开分析进行比较时,这个列表看起来非常接近家。

所以:由于没有最低密码强度要求,2%的用户使用前20个最常见的密码之一。这意味着:如果攻击者只有20次尝试,您网站上50个帐户中就有1个可以破解。

要解决这个问题,需要计算密码的熵,然后应用阈值。美国国家标准与技术研究所(NIST)特别出版物800-63有一组非常好的建议。当结合字典和键盘布局分析(例如,“qwertyuiop”是一个错误的密码)时,可以在18位熵的水平上拒绝99%的错误选择密码。简单地计算用户的密码强度和显示了视觉强度计是好的,但还不够。除非强制执行,否则很多用户很可能会忽略它。

为了使高熵密码的用户友好性令人耳目一新,强烈推荐Randall Munroe的密码强度xkcd

利用Troy Hunt的我是否被入侵了API来检查用户密码与公共数据泄露中泄露的密码。

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

首先,看一下数字:密码恢复速度-多久你的密码站起来

如果您没有时间查看该链接中的表格,以下是它们的列表:

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

  2. 如果是不区分大小写,则需要几乎没有时间才能破解9个字符的字母数字密码

  3. 它需要几乎没有时间破解一个复杂的,符号和字母和数字,大小写密码,如果它是长度少于8个字符(台式电脑可以搜索整个密钥空间多达7个字符在几天甚至几个小时)

  4. 然而,如果你被限制在每秒一次尝试,破解一个6个字符的密码需要花费过多的时间!

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

一般来说,你有三个选择,都是有效的蛮力攻击(和字典攻击,但由于您已经采用了强密码策略,因此它们不应该成为问题)

  • 在N次失败的尝试后呈现验证码(非常烦人并且经常无效-但我在这里重复自己)

  • 锁定账户并且在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最佳实践建议(必读列表中的链接1),应该被认为是最佳实践,即使它被公认是限制性的。

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

DoS攻击这种最终登录限制方案将是<强>非常不切实际。作为最后的接触,始终允许持久(cookie)登录(和/或验证码验证的登录表单)通过,这样合法用户甚至不会被延迟在攻击进行的同时。这样,非常不切实际的DoS攻击就变成了非常不切实际的攻击。

此外,对管理员帐户进行更积极的限制是有意义的,因为这些是最具吸引力的切入点

第七部分:分布式蛮力攻击

顺便说一句,更高级的攻击者将试图通过“传播他们的活动”来规避登录限制:

  • 分发僵尸网络上的尝试以防止IP地址标记

  • 他们不会选择一个用户并尝试50.000个最常见的密码(因为我们的限制,他们不能),而是选择最常见的密码并对50.000个用户进行尝试。这样,他们不仅可以绕过验证码和登录限制等最大尝试次数措施,他们成功的机会也会增加,因为排名第一的最常见密码远比49.995号更有可能

  • 间隔每个用户帐户的登录请求,例如,相隔30秒,以潜入雷达

在这里,最佳做法是记录系统范围内失败登录的数量,并使用您网站的不良登录频率的运行平均值作为对所有用户施加的上限的基础。

太抽象了?让我换个说法:

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

我还发布了一个关于更多的细节和一个非常好的讨论如何避免棘手的陷阱抵御分布式蛮力攻击的问题

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

凭证可能会受到损害,无论是通过漏洞利用、密码被写入和丢失、带有密钥的笔记本电脑被盗,还是用户进入钓鱼网站的登录。双重验证可以进一步保护登录,双重验证使用带外因素,例如从电话、短信、应用程序或加密狗接收到的一次性代码。一些提供商提供双重验证服务。

身份验证可以完全委托给单点登录服务,由另一个提供商处理收集凭据。这将问题推到受信任的第三方。谷歌和Twitter都提供基于标准的SSO服务,而Facebook提供类似的专有解决方案。

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

  1. OWASP认证指南/OWASP认证备忘单
  2. Web上客户端身份验证的注意事项(非常易读的麻省理工学院研究论文)
  3. 维基百科:HTTP cookie
  4. 后备身份验证的个人知识问题:Facebook时代的安全问题(非常可读性的伯克利研究论文)

首先,强烈警告,这个答案不是这个问题的最佳答案。它绝对不应该是最重要的答案!

我将继续提到Mozilla提出的浏览器ID(或者更准确地说,验证电子邮件协议),其精神是在未来找到更好的身份验证方法的升级路径。

我会这样总结:

  1. Mozilla是一个非营利组织,排名第0,与找到解决这个问题的好方法非常一致。
  2. 今天的现实是,大多数网站使用基于表单的身份验证
  3. 基于表单的身份验证有一个很大的缺点,即增加了钓鱼的风险。用户被要求将敏感信息输入到由远程实体控制的区域,而不是由其用户代理(浏览器)控制的区域。
  4. 由于浏览器是隐式信任的(用户代理的整个思想是代表用户行事),它们可以帮助改善这种情况。
  5. 这里阻碍进步的主要力量是部署死锁。解决方案必须分解成步骤,这些步骤本身就提供了一些增量收益。
  6. 表达互联网基础设施中内置的身份的最简单的去中心化方法是域名。
  7. 作为表示身份的第二层,每个域管理自己的一组帐户。
  8. 表单“帐户@域”简明扼要,并得到广泛的协议和URI方案的支持。当然,这样的标识符最普遍地被认为是电子邮件地址。
  9. 电子邮件提供者已经是事实上的在线主要身份提供者。如果您可以证明您控制了该帐户的关联电子邮件地址,则当前的密码重置流程通常允许您控制该帐户。
  10. 已验证电子邮件协议旨在提供一种基于公钥加密的安全方法,以简化向域B证明您在域A上拥有帐户的过程。
  11. 对于不支持验证电子邮件协议的浏览器(目前全部),Mozilla提供了一个shim,它在客户端JavaScript代码中实现了该协议。
  12. 对于不支持验证电子邮件协议的电子邮件服务,该协议允许第三方充当受信任的中介,断言他们已经验证了用户对帐户的所有权。不希望有大量这样的第三方;此功能仅用于允许升级路径,并且最好由电子邮件服务自己提供这些断言。
  13. Mozilla提供了自己的服务来充当此类受信任的第三方。实施验证电子邮件协议的服务提供商(即依赖方)可以选择是否信任Mozilla的断言。Mozilla的服务使用发送带有确认链接的电子邮件的传统方式验证用户的帐户所有权。
  14. 当然,服务提供商可以提供此协议作为他们可能希望提供的任何其他身份验证方法之外的选项。
  15. 这里寻求的一个大的用户交互界面是“身份选择器”。当用户访问一个网站并选择进行身份验证时,他们的浏览器会向他们显示一系列电子邮件地址(“个人”、“工作”、“政治活动”等),他们可以使用这些地址来识别自己的身份。
  16. 作为这项工作的一部分,正在寻求的另一个大的用户交互界面好处是帮助浏览器了解更多关于用户会话的信息-他们目前主要是谁登录的-因此它可以在浏览器chrome中显示。
  17. 由于该系统的分布式特性,它避免了锁定Facebook,Twitter,Google等主要网站,任何个人都可以拥有自己的域,因此充当自己的身份提供者。

这不是严格意义上的“基于表单的网站身份验证”。但它是为了从当前基于表单的身份验证规范过渡到更安全的东西:浏览器支持的身份验证。

我不认为上述答案是“错误的”,但有很大一部分的身份验证没有涉及到(或者更确切地说,重点是“如何实现cookie会话”,而不是“有哪些选项可用以及有哪些权衡”。

我建议的编辑/答案是

  • 问题更多地在于帐户设置而不是密码检查。
  • 使用双重验证比更聪明的密码加密手段更安全
  • 不要尝试实现自己的登录表单或密码的数据库存储,除非存储的数据在帐户创建和自我生成时毫无价值(即Facebook、flickr等web 2.0风格)

    1. 摘要身份验证是所有主要浏览器和服务器都支持的基于标准的方法,即使通过安全通道也不会发送密码。

这避免了任何需要“会话”或cookie,因为浏览器本身每次都会重新加密通信。这是最“轻量级”的开发方法。

但是,我不建议这样做,除了公共的、低价值的服务。这是上面其他一些答案的问题——不要尝试重新实现服务器端身份验证机制——这个问题已经解决,并且得到大多数主要浏览器的支持。不要使用cookie。不要在你自己的手工卷数据库中存储任何东西。只需询问每个请求,请求是否经过身份验证。其他一切都应该由配置和第三方可信软件支持。

那么…

首先,我们混淆了最初创建帐户(有密码)和随后重新检查密码。如果我是Flickr并且第一次创建您的网站,新用户可以访问零价值(空白网页空间)。我真的不在乎创建帐户的人是否在谎报姓名。如果我在医院内联网/外联网创建帐户,其价值在于所有医疗记录,因此我关心帐户创建者的身份(*)。

这是非常非常困难的部分。只有体面的解决方案是一个信任网络。例如,你以医生的身份加入医院。你创建一个托管在某处的网页,里面有你的照片、护照号码和公钥,并用私钥对它们进行哈希。然后你去医院,系统管理员查看你的护照,看看照片是否与你匹配,然后用医院私钥对网页/照片哈希进行哈希。从现在开始,我们可以安全地交换密钥和令牌。任何信任医院的人都可以(顺便说一句,有秘密酱)。系统管理员也可以给你一个RSA加密狗或其他双重验证。

但这是一个很多的麻烦,不是很web 2.0。然而,它是创建新帐户的唯一安全方法,这些帐户可以访问非自我创建的有价值信息。

  1. Kerberos和SPNEGO-单点登录机制与受信任的第三方-基本上是用户对受信任的第三方进行验证。(注意,这在任何方面都不是不可信的OAuth)

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

  3. SSL客户端-给客户端一个公钥证书(在所有主流浏览器支持-但提出了客户端机器安全性的问题)。

最终,这是一个权衡——安全漏洞的成本与实施更安全方法的成本是多少。有一天,我们可能会看到一个被广泛接受的正确的PKI,因此不再拥有自己的身份验证表单和数据库。有一天…

散列时,不要使用快速散列算法,例如MD5(存在许多硬件实现)。使用类似SHA-512的东西。对于密码,较慢的散列更好。

您创建哈希的速度越快,任何暴力检查器的工作速度就越快。因此,较慢的哈希将减慢暴力强制。缓慢的哈希算法将使暴力强制对于更长的密码(8位+)不切实际

我只是想分享这个我发现工作得很好的解决方案。

我称之为假人场(虽然我没有发明这个,所以不要相信我)。其他人知道这是一个蜜罐。

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

<input type="text" name="email" style="display:none" />

诀窍是欺骗机器人,让它认为它必须将数据插入到必填字段中,这就是为什么我将输入命名为“电子邮件”。如果你已经使用了一个名为电子邮件的字段,你应该尝试将虚拟字段命名为“公司”、“电话”或“电子邮件地址”等其他名称。只需选择一些你知道你不需要的东西,以及听起来像人们通常认为合乎逻辑的东西来填写网络表单。现在使用CSS或JavaScript/jQuery隐藏input字段——无论什么最适合你——只需不要将输入type设置为hidden,否则机器人不会上当。

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

示例:

在人类的情况下:用户不会看到虚拟字段(在我的情况下名为“email”)并且不会尝试填充它。因此,当表单发送时,虚拟字段的值仍然应该为空。

如果是bot:机器人将看到一个类型为text的字段和一个名称email(或者你叫它什么),并将在逻辑上尝试用适当的数据填充它。它不在乎你是否使用一些花哨的CSS来设计输入表单,Web开发人员一直在这样做。无论虚拟字段的值是什么,只要它大于0个字符,我们都不在乎。

我在留言簿上结合验证码使用了这种方法,从那以后我再也没有看到过一个垃圾邮件。我以前使用过仅验证码的解决方案,但最终,它导致每小时大约五个垃圾邮件。在表单中添加虚拟字段已经阻止了(至少到现在为止)所有垃圾邮件的出现。

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

警告:当然,这种方法不是100%万无一失的。可以对机器人进行编程,以忽略应用display:none样式的输入字段。您还必须考虑使用某种形式的自动完成(就像大多数浏览器内置的!)来自动填充所有表单字段的人。他们还不如选择一个虚拟字段。

您也可以通过将虚拟字段留在屏幕边界之外可见来稍微改变这一点,但这完全取决于您。

要有创意!

我最喜欢的关于身份验证系统的规则:使用密码,而不是密码。容易记住,很难破解。更多信息:编码恐怖:密码与通行证短语

我想补充一点非常重要的意见:

  • “在企业、内部净设置中,”上述大部分(如果不是全部)可能不适用!

许多公司部署“仅供内部使用”的网站,这些网站实际上是通过URL实现的“公司应用程序”。这些URL只能在“公司的内部网络”中解决。(该网络神奇地包括所有VPN连接的“公路战士”。

当用户尽职尽责地连接到上述网络时,他们的身份(“认证”)[已经…]“确凿地知道”,他们的权限(“授权”)做某些事情……例如……“访问本网站”。

这种“身份验证+授权”服务可以由几种不同的技术提供,例如LDAP(Microsoft OpenDirectory)或Kerberos。

从你的角度来看,你只是知道这一点:任何人合法地结束了你的网站必须伴随着[一个环境变量神奇地包含…]一个“令牌”(没有这样的令牌必须是404 Not Found的直接理由。

令牌的价值对你来说毫无意义,但是,如果需要,“存在适当的手段”,你的网站可以“[权威地]询问知道(LDAP…等)”你可能有的任何每一个(!)问题。换句话说,你做了没有利用任何“本土逻辑”。相反,你询问权威,并隐含地相信它的判决。

啊哈……这是相当来自“狂野和羊毛互联网”的精神开关。

我想添加一个我用过的建议,基于深度防御。你不需要像普通用户一样为管理员提供相同的auth&auth系统。你可以在一个单独的URL上有一个单独的登录表单,为授予高权限的请求执行单独的代码。这个可以做出对普通用户来说完全痛苦的选择。我用过的一个是实际上扰乱登录URL以供管理员访问,并通过电子邮件将新URL发送给管理员。立即停止任何暴力攻击,因为你的新URL可能是任意困难的(非常长的随机字符串),但你的管理员用户唯一的不便是跟随他们电子邮件中的链接。攻击者甚至不知道在哪里发布。

我不知道这是最好的答案还是评论,我选择了第一个选项。

关于第一个答案中的poing第四部分:忘记密码功能,我想谈谈定时攻击。

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

关于忘记密码表,我想补充一点,这是一个好主意,成功和不成功的查询之间的时间相等,具有一些延迟功能。

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

使用OpenID Connect用户管理访问

没有什么比什么都不做更有效。