权威的形成指南
基于表单的网站认证
我们认为堆栈溢出不应该只是针对非常具体的技术问题的资源,还应该针对如何解决常见问题变化的一般准则。 “基于表单的网站认证”应该成为这样一个实验的一个很好的主题。
它应该包括以下主题:
它不应该包括像这样的东西:
请帮助我们:
第一部分:如何登录
我们假定您已经知道如何构建登录名+密码HTML表单,该表单将值发布到服务器端的脚本以进行身份验证。 以下各节将讨论实际验证的模式,以及如何避免最常见的安全缺陷。
要HTTPS还是不HTTPS?
除非连接已经安全(即使用SSL / TLS通过HTTPS隧道传输),否则登录表单值将以明文形式发送,这样任何人在浏览器和Web服务器之间的窃听行为都将能够在登录过程中读取登录信息通过。 这种类型的窃听通常是由政府完成的,但总的来说,除了这么说之外,我们不会解决'拥有'的线路问题:如果您要保护任何重要内容,请使用HTTPS。
实质上,在登录期间防止窃听/数据包嗅探的唯一实用方法是使用HTTPS或其他基于证书的加密方案(例如TLS)或经过验证和测试的挑战 - 响应方案(例如,Diffie-Hellman基于SRP)。 窃听攻击者可以轻松绕过任何其他方法。
当然,如果你愿意做一些不切实际的事情,你也可以采用某种形式的双因素身份验证方案(例如Google身份验证器应用程序,物理“冷战风格”代码簿或RSA密钥生成器加密狗)。 如果正确应用,即使使用不安全的连接,也可以工作,但很难想象开发人员会愿意实施双因素身份验证而不是SSL。
(不要)滚动您自己的JavaScript加密/哈希
考虑到在您的网站上设置SSL证书的非零成本和感知技术难度,一些开发人员试图推出自己的浏览器内散列或加密方案,以避免在不安全线路上传递明文登录。
虽然这是一种崇高的想法,但它本质上是无用的(并且可能是一种安全缺陷),除非它与上述之一结合在一起 - 也就是说,要么使用高度加密来保护线路,要么使用经过验证的挑战 - 响应机制(如果你不知道它是什么,只需要知道它是最难以证明的,最难设计的,并且在数字安全中最难实现的概念)。
确实,哈希密码可以有效防范密码泄漏 ,但它很容易受到重播攻击,中间人攻击/劫持(如果攻击者可以在到达您的网页之前向您的不安全HTML页面中注入几个字节浏览器,他们可以简单地将JavaScript中的散列注释掉)或蛮力攻击(因为您正在向攻击者提供用户名,密码和散列密码)。
CAPTCHAS反对人性
CAPTCHA旨在阻止一种特定类型的攻击:自动字典/暴力试验和错误,没有人为操作员。 毫无疑问,这是一个真正的威胁,但是有些方法可以无缝处理,不需要验证码,特别是设计合理的服务器登录限制方案 - 我们将在后面讨论。
知道CAPTCHA的实现不是相同的; 他们往往不是人类可以解决的,他们中的大多数实际上对机器人无效,他们都对廉价的第三世界劳动力无效(根据OWASP,目前的血汗工厂率是每500个测试12美元),并且一些实施可能是技术上在某些国家是非法的(参见OWASP身份验证备忘单)。 如果您必须使用CAPTCHA,请使用Google的reCAPTCHA,因为它的定义是OCR-hard(因为它已经使用了OCR错误分类的书本扫描),并且很难做到用户友好。
就我个人而言,我倾向于发现验证码令人讨厌,并且只在用户多次登录失败并且限制延迟最大化时才将它们用作最后的手段。 这种情况很少会被接受,并且会加强整个系统。
存储密码/验证登录
在最近几年我们看到的所有高度被公开的黑客和用户数据泄漏之后,这可能最终是常识,但必须说:不要在数据库中以明文形式存储密码。 用户数据库通常会通过SQL注入被黑客攻击,泄露或收集,如果您正在存储原始密码,明文密码,那么您的登录安全性就是即时游戏。
因此,如果您无法存储密码,您如何检查登录表单中发送的登录名+密码组合是否正确? 答案是使用密钥派生函数进行散列。 无论何时创建新用户或更改密码,都需要输入密码并通过KDF(如Argon2,bcrypt,scrypt或PBKDF2)运行,将明文密码(“correcthorsebatterystaple”)转换为长且随机的字符串,这对存储在数据库中更安全。 要验证登录,您对输入的密码运行相同的散列函数,这次传入salt并将结果散列字符串与存储在数据库中的值进行比较。 Argon2,bcrypt和scrypt已经存储了哈希值。 查看sec.stackexchange上的这篇文章以获取更多详细信息。
使用盐的原因是因为散列本身并不足够 - 您需要添加所谓的“盐”以保护散列表不受到彩虹表的影响。 盐有效地防止两个完全匹配的密码被存储为相同的散列值,从而防止整个数据库在一次运行中被扫描,如果攻击者正在执行密码猜测攻击。
密码散列不应该用于密码存储,因为用户选择的密码不够强(即通常不包含足够的熵),而攻击者可以在相对较短的时间内通过访问散列来完成密码猜测攻击。 这就是使用KDF的原因 - 它们有效地“扩展密钥”,这意味着攻击者所做的每个密码猜测都涉及多次迭代哈希算法,例如10,000次,这使得攻击者的密码猜测速度减慢了10,000倍。
会话数据 - “您以Spiderman69身份登录”
一旦服务器验证了用户数据库的登录名和密码并找到匹配项,系统就需要记住浏览器已通过验证。 这个事实应该只存储在会话数据中的服务器端。
如果您不熟悉会话数据,请按照以下方式工作:将一个随机生成的字符串存储在即将到期的Cookie中,并用于引用存储在服务器上的数据集合 - 会话数据。 如果你使用的是MVC框架,这无疑已经被处理了。
如果可能,请确保会话cookie在发送到浏览器时设置了安全和HTTP Only标志。 httponly标志为防止XSS攻击读取的cookie提供了一些保护。 安全标志确保cookie仅通过HTTPS发回,因此可以防止网络嗅探攻击。 Cookie的价值不应该是可预测的。 在提交引用不存在的会话的cookie时,应立即替换其值以防止会话固定。
第二部分:如何保持登录 - 臭名昭着的“记住我”复选框
持久登录Cookies(“记住我”功能)是一个危险区域; 一方面,当用户理解如何处理它们时,它们与传统登录一样安全; 另一方面,它们对于粗心大意的用户来说是一种巨大的安全风险,他们可能会在公用计算机上使用它们并忘记注销,而且谁可能不知道浏览器cookie是什么或者如何删除它们。
就我个人而言,我喜欢定期访问我访问的网站的持久登录,但我知道如何安全地处理它们。 如果您肯定您的用户知道相同的内容,则可以使用持久登录以及良心清除。 如果没有 - 那么你可以订阅这样一种哲学:如果用户遭到黑客入侵,那么对于他们的登录凭证不慎的用户就会将其自身带入其中。 这不像我们去用户的房子,并用他们排列在显示器边缘的密码撕掉所有那些带面部皮肤的Post-It笔记。
当然,有些系统无法负担任何账户被盗用的情况; 对于这样的系统,你无法证明持久登录是合理的。
如果您决定实施持久性登录Cookie,您可以这样做:
首先,花一些时间阅读Paragon Initiative关于这个主题的文章。 你需要得到一些正确的元素,而且这篇文章在解释每个元素方面做得很好。
只是为了重申其中一个最常见的陷阱, 不要在您的数据库中存储持久登录Cookie(代币),只有它的哈希! 登录令牌是密码等效的,所以如果攻击者抓住你的数据库,他们可以使用令牌登录到任何帐户,就像它们是明文登录密码组合一样。 因此,在存储持久登录标记时,使用散列(根据https://security.stackexchange.com/a/63438/5002,弱散列可以很好地实现此目的)。
第三部分:使用秘密问题
不要执行'秘密问题' 。 “秘密问题”功能是一种安全反模式。 从MUST-READ列表中阅读4号链接的论文。 你可以问萨拉帕林关于那个,她的雅虎! 电子邮件帐户在之前的总统竞选期间遭到黑客攻击,因为她的安全问题的答案是......“瓦西拉高中”!
即使用户指定的问题,大多数用户很可能会选择:
一个'标准'秘密问题,如母亲的娘家姓或宠儿
任何人都可以从他们的博客,LinkedIn个人资料或类似内容中获得一些简单的琐事
任何比猜测密码更容易回答的问题。 对于任何体面的密码,哪一个是你能想象的问题
总而言之,安全问题在几乎所有的形式和变化中本质上是不安全的,并且不应该出于任何原因在认证方案中使用。
安全问题甚至存在的真正原因在于,它们可以方便地节省来自无法访问其电子邮件以获得重新激活代码的用户的一些支持呼叫的成本。 这以牺牲安全性和萨拉佩林的声誉为代价。 值得? 可能不会。
第四部分:忘记密码功能
我已经提到过,为什么你不应该使用安全问题来处理被遗忘/丢失的用户密码; 不言而喻,您绝对不应该通过电子邮件向用户发送他们的实际密码。 在这个领域至少还有两个常见的陷阱:
不要将忘记的密码重置为自动生成的强密码 - 这些密码出乎意料地难以记忆,这意味着用户必须将其更改或写下来 - 例如,在显示器边缘的亮黄色Post-It上。 不要设置新的密码,而应该让用户立即选择一个新密码 - 这正是他们想要的。
始终将丢失的密码代码/令牌散列在数据库中。 再次 ,这个代码是密码等效的另一个例子,所以它必须是散列的,以防攻击者抓住你的数据库。 当请求丢失密码代码时,将明文代码发送到用户的电子邮件地址,然后对其进行散列处理,将散列保存在数据库中 - 然后丢弃原始代码。 就像密码或持久登录令牌一样。
最后一点:确保你输入'丢失的密码'的界面至少和你的登录表单本身一样安全,否则攻击者只会用这个来获得访问权限。 确保生成很长的“丢失密码”(例如,16个区分大小写的字母数字字符)是一个好的开始,但考虑添加与登录表单本身相同的限制方案。
第五部分:检查密码强度
首先,您需要阅读这篇小文章以进行现实检查:500个最常用的密码
好吧,或许列表并不是任何系统上任何系统上最常用密码的典型列表,但它很好地说明了当没有强制执行策略时人们会选择密码的程度。 此外,当您将其与最近被盗密码的公开可用分析进行比较时,该列表看起来可怕。
因此:没有最低密码强度要求,2%的用户使用前20个最常用密码之一。 含义:如果攻击者只获得20次尝试,那么您网站上的50个帐户中有1个将会被破解。
阻止这需要计算密码的熵然后应用一个阈值。 国家标准与技术研究院(NIST)特别出版物800-63有一系列非常好的建议。 当与字典和键盘布局分析(例如,'qwertyuiop'是一个错误的密码)结合使用时,可以在18位熵的级别拒绝99%所有选择不当的密码。 简单地计算密码强度并向用户显示视觉强度计是好的,但不足。 除非强制执行,否则很多用户很可能会忽略它。
为了让用户更方便地使用高熵密码,强烈建议使用Randall Munroe的密码强度xkcd。
第六部分:更多 - 或者:防止快速登录尝试
首先,看看这些数字:密码恢复速度 - 你的密码会持续多久
如果您没有时间查看该链接中的表格,请看下面的列表:
几乎没有时间来破解弱密码,即使你用算盘来破解密码
如果不区分大小写 ,几乎不需要破解字母数字的9个字符的密码
如果长度小于8个字符 ,则几乎不需要时间来破解复杂的符号和字母及数字,大小写密码(桌面PC可以在一个字符中搜索最多7个字符的整个密钥空间几天甚至几小时)
但是,如果您每秒限制一次尝试,它将花费过多时间来破解6个字符的密码!
那么我们可以从这些数字中学到什么? 好吧,但是我们可以专注于最重要的部分:防止大量连续登录尝试(即强力攻击)的事实确实并不困难。 但是防止它的正确性并不像看起来那么容易。
一般来说,您有三种选择对于强力攻击(以及字典攻击都是有效的,但由于您已经在使用强密码策略,因此它们不应该成为问题):
N次尝试失败后出示CAPTCHA (烦人地狱,经常无效 - 但我在此重复)
锁定帐户并在N次尝试失败后要求电子邮件验证(这是一个等待发生的DoS攻击)
最后, 登录限制 :即在N次尝试失败后设置尝试之间的时间延迟(是的,DoS攻击仍然可行,但至少它们可能性更小,并且更难复原)。
最佳做法#1:短时间延迟随失败尝试的次数而增加,如:
DoS攻击这个方案将是非常不切实际的,因为所产生的锁定时间比先前锁定时间的总和稍大。
澄清:在将响应返回给浏览器之前,延迟不是延迟。 这更像是一个超时或不应期,在此期间,登录尝试登录特定帐户或从特定IP地址登录时,根本不会被接受或评估。 也就是说,正确的凭证不会在成功登录时返回,而不正确的凭证不会延迟增加。
最佳做法#2: N次失败尝试后生效的中等长度时间延迟,如:
DoS攻击这个方案将是非常不切实际的,但肯定是可行的。 另外,可能需要注意的是,如此长时间的延迟对于合法用户来说可能非常烦人。 健忘的用户会不喜欢你。
最佳实践#3:结合这两种方法 - 要么是在N次失败尝试后生效的固定的短时间延迟,如:
或者,具有固定上限的递增延迟,如:
这个最终方案取自OWASP最佳实践建议(MUST-READ列表中的链接1),并且应该被认为是最佳实践,即使它被承认在限制性方面。
然而,作为一个经验法则,我会说:密码策略越强,用户拖延时间就越少。 如果您需要强大的(区分大小写的字母数字+必需的数字和符号)9位以上的字符密码,您可以在激活限制之前为用户提供2-4个非延迟密码尝试。
DoS攻击这个最终的登录节流方案将是非常不切实际的。 作为最后一步,始终允许持久性(cookie)登录(和/或验证码验证的登录表单)通过,这样合法用户甚至不会在攻击正在进行时被延迟。 这样,非常不切实际的DoS攻击变得非常不切实际的攻击。
另外,对管理帐户进行更积极的限制是有意义的,因为这些是最有吸引力的入口点
第七部分:分布式蛮力攻击
就像旁边一样,更高级的攻击者会试图通过“传播他们的活动”来规避登录限制:
分发僵尸网络上的尝试以防止IP地址标记
与其挑选一个用户并尝试50,000个最常用的密码(他们不能,因为我们的限制),他们会选择最常用的密码,而不是针对50,000个用户进行尝试。 这样,他们不仅能够绕过CAPTCHA和登录限制等最大尝试措施,而且他们的成功机会也会增加,因为1号最常用的密码比49.995号更有可能
将每个用户帐户的登录请求间隔30秒,以躲避雷达
在此,最佳做法是记录系统范围内失败登录的次数 ,并将站点恶意登录频率的运行平均值作为您对所有用户施加上限的基础。
太抽象了? 让我改述一下:
假设您的网站在过去3个月内平均每天有120次登录错误。 使用这个(运行平均值),你的系统可能会将全局限制设置为3倍 - 即。 在24小时内360次失败尝试。 然后,如果所有帐户的失败尝试总次数在一天内(或者甚至更好)超过该数字(监控加速率并在计算的阈值上触发),它会激活系统范围的登录限制 - 这意味着所有用户的短暂延迟(仍然是cookie登录和/或备份验证码登录除外)。
我还发布了一个更详细的问题和一个非常好的讨论,如何避免棘手的瑕疵来抵御分布式蛮力攻击
第八部分:双因素认证和认证提供者
证书可能会受到攻击,无论是漏洞攻击,密码被写下并丢失,钥匙被盗用的笔记本电脑或用户登录钓鱼网站。 登录可以通过双因素身份验证进一步得到保护,双因素身份验证使用带外因素,例如通过电话,SMS消息,应用程序或加密狗收到的一次性代码。 多家提供商提供双因素认证服务。
身份验证可以完全委托给单点登录服务,其中另一个提供程序处理收集凭证。 这将问题推给了可信的第三方。 Google和Twitter都提供基于标准的SSO服务,而Facebook则提供了类似的专有解决方案。
必须阅读关于Web认证的链接
权威性文章
发送凭证
发送凭证100%安全的唯一可行方法是使用SSL。 使用JavaScript散列密码并不安全。 客户端密码哈希的常见缺陷:
还有另一种称为SRP的安全方法,但它是获得专利的(虽然它是免费许可的),并且很少有好的实现可用。
存储密码
千万不要将密码存储为数据库中的明文。 即使你不关心你自己的网站的安全。 假设您的部分用户将重复使用其在线银行帐户的密码。 因此,存储哈希密码,并扔掉原来的。 并确保密码不会显示在访问日志或应用程序日志中。 OWASP建议使用Argon2作为新应用的首选。 如果这不可用,应该使用PBKDF2或scrypt。 最后,如果以上都不可用,请使用bcrypt。
哈希本身也是不安全的。 例如,相同的密码表示相同的哈希值 - 这使得哈希查找表成为一次破解大量密码的有效方法。 相反,存储盐渍散列。 salt是在散列之前附加到密码的字符串 - 为每个用户使用不同的(随机)盐。 salt是一个公共值,因此您可以将它们与散列一起存储在数据库中。 有关详情,请参阅此处。
这意味着你不能发送用户忘记密码(因为你只有散列)。 除非您已经对用户进行了身份验证,否则不要重置用户的密码(用户必须证明他们能够读取发送到存储(和验证的)电子邮件地址的电子邮件。)
安全问题
安全问题是不安全的 - 避免使用它们。 为什么? 任何安全问题都会影响密码的性能。 阅读第三部分:在@Jens Roland中使用秘密问题在这个wiki中回答这个问题。
会话Cookie
用户登录后,服务器向用户发送会话cookie。 服务器可以从cookie中检索用户名或id,但没有其他人可以生成这样的cookie(TODO解释机制)。
Cookies可能被劫持:它们只与客户的其他机器和其他通信一样安全。 它们可以从磁盘读取,在网络流量中嗅探,通过跨站点脚本攻击解决,从中毒的DNS中捕获,以便客户端将其cookie发送到错误的服务器。 不要发送永久性cookies。 Cookie应在客户端会话结束时结束(浏览器关闭或离开您的域)。
如果你想自动登录你的用户,你可以设置一个持久性cookie,但它应该与完整会话cookie不同。 您可以设置用户自动登录的附加标志,并且需要登录才能进行敏感操作。 这是购物网站,希望为您提供一个无缝的,个性化的购物体验,但仍然保护您的财务细节。 例如,当您返回访问亚马逊时,他们会向您显示一个看起来像您已登录的页面,但当您下订单(或更改您的送货地址,信用卡等)时,他们会要求您确认你的密码。
另一方面,诸如银行和信用卡等金融网站只有敏感数据,不应该允许自动登录或低安全模式。
外部资源列表
21页的学术文章与许多伟大的提示。
关于这个问题的论坛讨论
关于存储密码的介绍性文章
关于编码恐怖文章的论坛讨论。
关于在数据库中存储密码的另一个警告。
维基百科关于几个密码散列方案弱点的文章。
讨论关于彩虹桌,以及如何防御彩虹桌以及其他线索。 包括广泛的讨论。
首先,强烈警告说这个答案不适合这个确切的问题。 它绝对不应该是最好的答案!
我将继续提及Mozilla提出的BrowserID(或者更准确地说,验证电子邮件协议),以寻找升级途径,以便在未来实现更好的身份验证方法。
我会这样总结一下:
@
domain”形式简洁并且由各种协议和URI方案支持。 当然,这样的标识符通常被公认为电子邮件地址。 这不是严格的“基于表单的网站认证”。 但是,从目前的基于表单的身份验证规范过渡到更安全的浏览器支持身份验证是一项努力。
链接地址: http://www.djcxy.com/p/53.html上一篇: The definitive guide to form
下一篇: What is the difference between String and string in C#?