我们认为Stack Overflow不仅应该是非常具体的技术问题的资源,而且还应该是关于如何解决常见问题变化的一般指导原则。 “基于表单的网站身份验证”应该是这种实验的一个很好的主题。
我们假设您已经知道如何构建登录+密码HTML表单,该表单将值POST到服务器端的脚本以进行身份验证。下面的部分将讨论声音实用auth的模式,以及如何避免最常见的安全陷阱。
HTTPS还是HTTPS?
除非连接已经安全(即使用SSL / TLS通过HTTPS进行隧道传输),否则您的登录表单值将以明文形式发送,允许任何窃听浏览器和Web服务器之间的线路的人都可以在通过时读取登录信息通过。这种类型的窃听是由政府定期完成的,但总的来说,除了这样说之外,我们不会解决“拥有”的电话:只使用HTTPS。
实质上,在登录期间防止窃听/数据包嗅探的唯一实用方法是使用HTTPS或其他基于证书的加密方案(例如,TLS)或经过验证和测试的质询 - 响应方案(例如,基于Diffie-Hellman的方案) SRP)。窃听攻击者可以轻易绕过任何其他方法。
当然,如果你愿意有点不切实际,你也可以使用某种形式的双因素身份验证方案(例如Google身份验证器应用程序,物理“冷战风格”代码簿或RSA密钥生成器加密狗)。如果应用正确,即使使用不安全的连接,这也可以工作,但是很难想象开发人员愿意实现双因素身份验证而不是SSL。
(不)滚动自己的JavaScript加密/散列
鉴于在您的网站上设置SSL证书的成本和技术难度(虽然现在avoidable),一些开发人员很想推出他们自己的浏览器内哈希或加密方案,以避免通过不安全的线路传递明文登录。
虽然这是一个崇高的想法,但它基本上是无用的(并且可以是security flaw),除非它与上述之一相结合 - 即用强加密保护线路或使用经过试验和测试的挑战 - 响应机制(如果你不知道那是什么,只要知道它是最难以证明的,最难设计的,也是最难实现的数字安全概念)。
虽然散列密码可以有效防止密码泄露,但它很容易受到重放攻击,中间人攻击/劫持(如果攻击者可以在你的不安全的HTML页面到达你的之前注入几个字节)浏览器,他们可以简单地注释掉JavaScript中的哈希),或者暴力攻击(因为你正在向攻击者发送用户名,盐和哈希密码)。
CAPTCHAS反人类
CAPTCHA旨在阻止一种特定类型的攻击:自动字典/暴力破解试验和错误,没有人类操作员。毫无疑问,这是一个真正的威胁,但是,有一些方法可以无缝地处理它,不需要CAPTCHA,特别是正确设计的服务器端登录限制方案 - 我们稍后会讨论它们。
知道CAPTCHA实现不是一样的;它们通常不是人类可以解决的,其中大多数实际上对机器人无效,所有这些都对廉价的第三世界劳动力无效(根据OWASP,目前的血汗工厂率为每500次测试12美元),并且一些实施可能是在某些国家,在技术上是非法的(见OWASP Authentication Cheat Sheet)。如果您必须使用CAPTCHA,请使用Google的reCAPTCHA,因为它的定义是OCR-hard(因为它使用已经过OCR错误分类的书籍扫描),并且非常努力地使用户友好。
就个人而言,我倾向于发现CAPTCHAS很烦人,并且当用户多次登录失败并且节流延迟最大化时,仅将其用作最后的手段。这种情况很少发生,可以接受,并且整个系统都得到了加强。
存储密码/验证登录
在我们近年来看到的所有高度公开的黑客攻击和用户数据泄漏之后,这可能最终是常识,但必须说明:不要在数据库中以明文形式存储密码。用户数据库通常会通过SQL注入被黑客入侵,泄露或收集,如果您要存储原始明文密码,那么即时游戏结束也是为了您的登录安全性。
因此,如果您无法存储密码,如何检查登录表单中登录的登录名+密码组合是否正确?答案是使用key derivation function进行散列。无论何时创建新用户或更改密码,您都会获取密码并通过KDF运行,例如Argon2,bcrypt,scrypt或PBKDF2,将明文密码(“correcthorsebatterystaple”)转换为长而随机的字符串,这在您的数据库中存储更安全。要验证登录,请对输入的密码运行相同的哈希函数,这次传入salt并将生成的哈希字符串与存储在数据库中的值进行比较。 Argon2,bcrypt和scrypt已经将哈希值与哈希一起存储。有关更多详细信息,请查看sec.stackexchange上的article。
使用salt的原因是散列本身是不够的 - 你需要添加一个所谓的'salt'来保护散列不受rainbow tables的影响。 salt有效地防止两个完全匹配的密码被存储为相同的哈希值,从而防止在攻击者正在执行密码猜测攻击时在一次运行中扫描整个数据库。
加密哈希不应该用于密码存储,因为用户选择的密码不够强(即通常不包含足够的熵),并且密码猜测攻击可以在相对较短的时间内由访问哈希的攻击者完成。这就是使用KDF的原因 - 这些有效的"stretch the key",这意味着攻击者所做的每个密码都会导致哈希算法的多次重复,例如10,000次,这会导致攻击者将密码猜测速度减慢10,000倍。
会话数据 - “您以Spiderman69身份登录”
一旦服务器针对您的用户数据库验证了登录名和密码并找到匹配项,系统就需要一种方法来记住浏览器已经过身份验证。这个事实应该只在服务器端存储在会话数据中。
如果您不熟悉会话数据,可以使用以下方法:单个随机生成的字符串存储在过期的cookie中,用于引用存储在服务器上的数据集合 - 会话数据。如果您使用的是MVC框架,那么这无疑已经得到了处理。
如果可能,请确保会话cookie在发送到浏览器时设置了安全且仅HTTP标记。 HttpOnly标志提供一些保护,防止通过XSS攻击读取cookie。安全标志确保cookie仅通过HTTPS发回,因此可防止网络嗅探攻击。 cookie的值不应该是可预测的。如果呈现引用不存在的会话的cookie,则应立即替换其值以防止session fixation。
持久登录Cookie(“记住我”功能)是一个危险区域;一方面,当用户了解如何处理它们时,它们与传统登录完全一样安全;而另一方面,它们在粗心用户手中是一个巨大的安全风险,他们可能在公共计算机上使用它们而忘记注销,并且可能不知道浏览器cookie是什么或如何删除它们。
就个人而言,我喜欢定期访问的网站的持久登录,但我知道如何安全地处理它们。如果您肯定您的用户知道相同的内容,您可以使用持久登录并保持良心。如果没有 - 那么,你可以赞同这样的理念:如果用户被登录,那么他们的登录凭证会不小心。这并不像我们去我们用户的房子,并且用他们在显示器边缘排列的密码撕下所有那些引人注目的便利贴。
当然,有些系统无法承受任何帐户入侵;对于这样的系统,你无法证明持久登录的合理性。
如果您决定实施持久登录cookie,请按以下步骤操作:
不要实施“秘密问题”。 “秘密问题”功能是一种安全反模式。阅读MUST-READ列表中链接号为4的论文。在雅虎之后你可以向萨拉佩林询问这个问题。电子邮件帐户在之前的总统竞选期间被黑了,因为她的安全问题的答案是......“Wasilla High School”!
即使有用户指定的问题,大多数用户很可能会选择:
总之,安全问题本质上在所有形式和变体中都是不安全的,不应出于任何原因在认证方案中使用。
安全问题甚至存在于野外的真正原因是它们可以方便地节省来自无法访问其电子邮件以获得重新激活代码的用户的一些支持呼叫的成本。这是以牺牲安全性和Sarah Palin的声誉为代价的。值得?可能不是。
我已经提到为什么你不应该使用安全问题来处理忘记/丢失的用户密码;不言而喻,您绝不应该向用户发送实际密码。在这个领域至少还有两个常见的陷阱需要避免:
最后一点:始终确保用于输入“丢失的密码代码”的界面至少与登录表单本身一样安全,或者攻击者只是使用它来获取访问权限。确保生成很长的“丢失密码代码”(例如,16个区分大小写的字母数字字符)是一个良好的开端,但请考虑添加与登录表单本身相同的限制方案。
首先,您需要阅读这篇小文章进行现实检查:The 500 most common passwords
好的,所以这个列表可能不是任何地方任何系统上最常见密码的规范列表,但它很好地表明当没有强制执行的策略时,人们选择密码的能力有多差。此外,当您将该列表与最近被盗密码的公开分析进行比较时,该列表看起来非常贴近家庭。
因此:没有最低密码强度要求,2%的用户使用前20个最常用的密码之一。含义:如果攻击者只获得20次尝试,则您网站上50个帐户中的1个将被破解。
阻止这一点需要计算密码的熵,然后应用阈值。美国国家标准与技术研究院(NIST)Special Publication 800-63提出了一系列非常好的建议。当与字典和键盘布局分析相结合时(例如,'qwertyuiop'是一个错误的密码),可以使reject 99% of all poorly selected passwords处于18位熵的水平。简单地为用户计算密码强度和showing a visual strength meter是好的,但不够。除非强制执行,否则很多用户很可能会忽略它。
为了让用户更加熟悉高熵密码,强烈建议使用Randall Munroe的Password Strength xkcd。
利用Troy Hunt的Have I Been Pwned API检查用户密码,防止在公共数据泄露中泄露的密码。
首先,看看数字:Password Recovery Speeds - How long will your password stand up
如果您没有时间浏览该链接中的表,请参阅以下列表:
那么我们可以从这些数字中学到什么呢?好吧,很多,但我们可以专注于最重要的部分:防止大量快速连续登录尝试(即暴力攻击)的事实并不那么困难。但是正确地防止它并不像看起来那么容易。
一般来说,您有三种选择可以有效抵御暴力攻击(和字典攻击,但由于您已经采用强密码策略,它们应该不是问题):
最佳实践#1:短暂的时间延迟随着尝试失败的次数而增加,例如:
DoS攻击这个方案是非常不切实际的,因为结果锁定时间略大于先前锁定时间的总和。
澄清一下:在将响应返回给浏览器之前,延迟不是延迟。它更像是超时或不应期,在此期间,根本不会接受或评估特定帐户或特定IP地址的登录尝试。也就是说,正确的凭据不会在成功登录时返回,并且不正确的凭据不会触发延迟增加。
最佳实践#2:N次尝试失败后生效的中等时间延迟,如:
DoS攻击这个计划是不切实际的,但肯定是可行的。此外,可能需要注意的是,如此长的延迟对于合法用户来说可能非常烦人。忘记用户会不喜欢你。
最佳实践#3:结合两种方法 - 在N次尝试失败后生效的固定短时间延迟,例如:
或者,具有固定上限的增加延迟,例如:
这个最终方案取自OWASP最佳实践建议(来自MUST-READ列表的链接1),并且应该被认为是最佳实践,即使它是公认的限制性方面。
但是,根据经验,我会说:您的密码策略越强,您就越不会对延迟的用户造成错误。如果您需要强大的(区分大小写的字母数字+所需的数字和符号)9个以上的字符密码,您可以在激活限制之前为用户提供2-4次非延迟密码尝试。
DoS攻击这个最终登录限制方案将是非常不切实际的。作为最后的触摸,始终允许持久(cookie)登录(和/或验证CAPTCHA的登录表单)通过,因此合法用户甚至不会在攻击进行中被延迟。这样,非常不切实际的DoS攻击变成了一种非常不切实际的攻击。
此外,对管理员帐户进行更积极的限制是有意义的,因为这些是最具吸引力的切入点
同样,更高级的攻击者会试图通过“传播他们的活动”来规避登录限制:
在这里,最佳做法是记录系统范围内的失败登录次数,并使用站点的错误登录频率的运行平均值作为您对所有用户施加的上限的基础。
太抽象了?让我重新说一下:
假设您的网站在过去3个月内平均每天有120次不良登录。使用它(运行平均值),您的系统可能将全局限制设置为3倍 - 即。在24小时内360尝试失败。然后,如果所有帐户中的失败尝试总数在一天内超过该数量(甚至更好,监控加速率并触发计算的阈值),则会激活系统范围的登录限制 - 这意味着所有用户的短暂延迟(仍然,cookie登录和/或备份CAPTCHA登录除外)。
我还向more details and a really good discussion of how to avoid tricky pitfals发布了一个问题,以抵御分布式暴力攻击
凭据可能会受到损害,无论是利用漏洞,密码被记录下来还是丢失,密钥被盗的笔记本电脑,还是用户登录钓鱼网站。可以使用双因素身份验证进一步保护登录,该身份验证使用带外因素,例如通过电话,SMS消息,应用程序或加密狗接收的一次性代码。一些提供商提供双因素身份验证服务。
身份验证可以完全委托给单点登录服务,其中另一个提供程序处理收集凭据。这会将问题推送给受信任的第三方。谷歌和Twitter都提供基于标准的SSO服务,而Facebook则提供类似的专有解决方案。
我不知道最好回答这个问题作为答案还是评论。我选择了第一个选项。
关于第四部分:在第一个答案中忘记密码功能,我会指出时间攻击。
在“记住您的密码”表单中,攻击者可能会检查完整的电子邮件列表并检测哪些已注册到系统(请参阅下面的链接)。
关于遗忘密码表格,我想补充说,使用一些延迟功能在成功和不成功查询之间等于时间是个好主意。
我想补充一点非常重要的评论: -
许多公司部署“仅限内部使用”的网站,这些网站实际上是恰好通过URL实现的“公司应用程序”。这些URL可以(据说......)仅在“公司内部网络”中解析。 (哪个网络神奇地包括所有与VPN连接的“公路战士”。)
当用户尽职地连接到上述网络时,他们的身份(“身份验证”)[已经......]最终知道,“他们允许(”授权“)做某些事情......例如。 ..“访问这个网站。”
这种“身份验证+授权”服务可以由几种不同的技术提供,例如LDAP(Microsoft OpenDirectory)或Kerberos。
从您的观点来看,您只需知道:任何合法地在您的网站上结束的人必须伴随着[环境变量神奇地包含......]“令牌”。 (即没有这样的标记必须是404 Not Found
的直接理由。)
令牌的价值对你没有意义,但是,如果需要,“存在适当的手段”,你的网站可以“[权威地]询问知道(LDAP ......等)的人”任何一个(!)你可能有的问题。换句话说,你没有利用任何“本土逻辑”。相反,你询问管理局并暗中信任其判决。
嗯......这是一个从“野性和毛茸茸的互联网”转变为精神的过程。
使用OpenID Connect或User-Managed Access。
因为没有比没有做更有效的事情。
100%安全地发送凭证的唯一实用方法是使用SSL。使用JavaScript来散列密码是不安全的。客户端密码散列的常见缺陷:
还有另一种称为SRP的安全方法,但它已获得专利(尽管它是freely licensed)并且几乎没有很好的实现可用。
不要将密码作为纯文本存储在数据库中。即使您不关心自己网站的安全性也不会。假设您的某些用户将重复使用其在线银行帐户的密码。因此,存储散列密码,并丢弃原始密码。并确保密码不会显示在访问日志或应用程序日志中。 OWASP recommends the use of Argon2是您新应用的首选。如果没有,则应使用PBKDF2或scrypt。最后,如果以上都不可用,请使用bcrypt。
哈希本身也是不安全的。例如,相同的密码意味着相同的哈希 - 这使得哈希查找表成为一次破解大量密码的有效方法。相反,存储盐渍哈希。 salt是在散列之前附加到密码的字符串 - 每个用户使用不同的(随机)salt。 salt是一个公共值,因此您可以将哈希值存储在数据库中。有关详细信息,请参阅here。
这意味着您无法向用户发送他们忘记的密码(因为您只有哈希)。除非您已对用户进行身份验证,否则不要重置用户的密码(用户必须证明他们能够阅读发送到存储(和验证的)电子邮件地址的电子邮件。)
安全问题是不安全的 - 避免使用它们。为什么?安全问题,密码做得更好。阅读第三部分:在这个wiki中使用@Jens Roland answer中的秘密问题。
用户登录后,服务器会向用户发送会话cookie。服务器可以从cookie中检索用户名或id,但是没有其他人可以生成这样的cookie(TODO解释机制)。
Cookies can be hijacked:它们只能与客户机器和其他通信的其他部分一样安全。它们可以从磁盘读取,在网络流量中嗅探,由跨站点脚本攻击解除,从中毒的DNS中删除,以便客户端将其cookie发送到错误的服务器。不要发送持久性cookie。 Cookie应在客户端会话结束时到期(浏览器关闭或离开您的域)。
如果要对用户进行自动登录,可以设置持久性cookie,但它应与完整会话cookie不同。您可以设置用户已自动登录的附加标志,并且需要登录才能进行敏感操作。这对于希望为您提供无缝,个性化购物体验但仍能保护您的财务详细信息的购物网站而言非常受欢迎。例如,当您返回访问亚马逊时,他们会向您显示一个看起来像您已登录的页面,但当您下订单(或更改您的送货地址,信用卡等)时,他们会要求您确认你的密码。
另一方面,银行和信用卡等金融网站只有敏感数据,不允许自动登录或低安全模式。
首先,一个强烈的警告,这个答案不是最适合这个问题的答案。绝对不应该是最好的答案!
我将继续提及Mozilla提出的BrowserID(或者更确切地说,Verified Email Protocol),其目的是寻找未来更好的身份验证方法的升级途径。
我将这样总结一下:
@
domain”形式简洁明了,并受到各种协议和URI方案的支持。当然,这种标识符最普遍地被认为是电子邮件地址。这不是严格的“基于表单的网站身份验证”。但是,努力从基于表单的身份验证的当前规范过渡到更安全的东西:浏览器支持的身份验证。
我只是觉得我会分享这个我发现工作得很好的解决方案。
我称之为Dummy Field(虽然我没有发明这个,所以不要相信我)。
简而言之:您只需将其插入<form>
并在验证时检查它是否为空:
<input type="text" name="email" style="display:none" />
诀窍是欺骗机器人认为必须将数据插入必填字段,这就是为什么我将输入命名为“电子邮件”。如果您已经有一个名为email的字段,那么您应该尝试将虚拟字段命名为“company”,“phone”或“emailaddress”。只需选择一些您不需要的东西,以及人们通常认为可以填写到网络表单中的东西。现在使用CSS或JavaScript / jQuery隐藏input
字段 - 无论什么最适合你 - 只是不要将输入type
设置为hidden
,否则机器人不会因此而失败。
当您验证表单(客户端或服务器端)时,检查您的虚拟字段是否已填满,以确定它是由人还是机器人发送的。
例:
如果是人:用户将看不到虚拟字段(在我的情况下命名为“email”)并且不会尝试填充它。因此,在发送表单时,虚拟字段的值仍应为空。
在机器人的情况下:机器人将看到一个类型为text
的字段和一个名称email
(或者你称之为它的任何东西)并且将在逻辑上尝试用适当的数据填充它。如果您使用一些花哨的CSS设置输入表单的样式并不关心,Web开发人员会一直这样做。无论虚拟场中的值是什么,只要它比0
字符大,我们就不在乎。
我在留言簿上使用这种方法与CAPTCHA结合使用,之后我没有看过任何一封垃圾邮件帖子。我之前使用过CAPTCHA解决方案,但最终每小时产生了大约5个垃圾邮件。在表单中添加虚拟字段已停止(至少到现在为止)所有垃圾邮件都会出现。
我相信这也可以用于登录/身份验证表单。
警告:当然这种方法并非100%万无一失。可以将机器人编程为忽略应用了display:none
样式的输入字段。您还必须考虑使用某种形式的自动完成功能的人(比如大多数浏览器都内置了!)来自动填充所有表单字段。他们可能也会选择一个虚拟场。
您也可以通过让虚拟区域可见但在屏幕边界之外可以稍微改变一下,但这完全取决于您。
要有创意!
我不认为上面的答案是“错误的”,但是有大量的认证没有被触及(或者更重要的是“如何实现cookie会话”,而不是“有哪些选项可用,什么是交易” -offs”。
我建议的编辑/答案是
这避免了任何需要“会话”或cookie的需要,因为浏览器本身每次都会重新加密通信。它是最“轻量级”的开发方法。
但是,除了公共的低价值服务之外,我不建议这样做。这是上面一些其他答案的问题 - 不要尝试重新实现服务器端身份验证机制 - 这个问题已经解决并且得到大多数主流浏览器的支持。不要使用cookies。不要在您自己的手工数据库中存储任何内容。根据请求,只询问请求是否经过身份验证。配置和第三方可信软件应支持其他所有功能。
所以......
首先,我们将初始创建帐户(带密码)与随后重新检查密码混淆。如果我是Flickr并且是第一次创建您的站点,则新用户可以访问零值(空白Web空间)。如果创建帐户的人谎称他们的名字,我真的不在乎。如果我正在创建医院内联网/外联网的帐户,则价值在于所有医疗记录,因此我关心帐户创建者的身份(*)。
这是非常困难的部分。唯一合适的解决方案是信任网。例如,您作为医生加入医院。您创建一个托管在您的照片,护照号码和公钥的某个地方的网页,并使用私钥对它们进行哈希处理。然后,您访问医院,系统管理员查看您的护照,查看照片是否与您匹配,然后使用医院私钥对网页/照片哈希进行哈希处理。从现在开始,我们可以安全地交换密钥和令牌。任何信任医院的人都可以(有秘密酱BTW)。系统管理员还可以为您提供RSA加密狗或其他双因素身份验证。
但这很麻烦,而不是非常网络2.0。但是,它是创建可以访问非自创的有价值信息的新帐户的唯一安全方法。
最后,这是一个权衡 - 安全漏洞的成本与实施更安全的方法的成本是什么。有一天,我们可能会看到一个被广泛接受的正确的PKI,因此不再拥有自己的滚动身份验证表单和数据库。一天...
散列时,不要使用快速哈希算法,如MD5(存在许多硬件实现)。使用像SHA-512这样的东西。对于密码,较慢的哈希值更好。
创建哈希值的速度越快,任何暴力检查程序的工作速度就越快。因此较慢的哈希会减慢暴力迫使。慢速哈希算法会使更长时间的密码(8位+)变得不切实际
关于身份验证系统我最喜欢的规则:使用密码而不是密码。容易记住,难以破解。更多信息:Coding Horror: Passwords vs. Pass Phrases
我想根据防御深度添加一个我用过的建议。您不需要为常规用户提供与管理员相同的auth&auth系统。您可以在单独的URL上使用单独的登录表单,为要授予高权限的请求执行单独的代码。这个可以做出对普通用户来说完全痛苦的选择。我使用的一个实际上是加密管理员访问的登录URL并通过电子邮件向管理员发送新URL。立即停止任何暴力攻击,因为您的新URL可能是任意困难的(非常长的随机字符串),但您的管理员用户唯一的不便是在他们的电子邮件中跟踪链接。攻击者不再知道POST到哪里。