基于表单的网站身份验证
我们认为,堆栈溢出不仅应该是非常具体的技术问题的资源,而且应该是关于如何解决常见问题变化的一般指南。"基于表单的网站认证"应该是此类实验的一个很好的主题。
它应该包括以下主题:
- 如何登录
- 如何注销
- 如何保持登录状态
- 管理cookie(包括推荐的设置)
- SSL/HTTPS加密
- 如何存储密码
- 使用秘密问题
- 忘记用户名/密码功能
- 使用nonce防止跨站点请求伪造(csrf)
- OpenID
- "记住我"复选框
- 浏览器自动完成用户名和密码
- 机密URL(由摘要保护的公共URL)
- 检查密码强度
- 电子邮件验证
- 更多关于基于表单的身份验证…
不应包括以下内容:
请通过以下方式帮助我们:
暗示副标题
提交关于这个主题的好文章
编辑官方答案
- 为什么要排除HTTP基本身份验证?它可以通过ajax:peej.co.uk/articles/http-auth-with-html-forms.html在HTML表单中工作。
- HTTP基本身份验证具有(相对而言)很难让浏览器忘记的特性。如果您不将它与ssl一起使用来保护连接(即https),那么它也会非常不安全。
- 我认为值得讨论的是会话(包括固定和劫持)cookie(安全和仅HTTP标志)基于HTTP的SSO
- 如果你的密码是经过编译的,那么就可以减少字典攻击-en.wikipedia.org/wiki/key_强化
- 超级有用的HttpOnlycookie标志,它可以防止基于javascript的cookie盗窃(XSS攻击的一个子集),也应该在某个地方提到。
- 我们可能应该有一个最佳实践标签或类似的东西来回答这样的优秀问题和答案。
- 引用实现?
- 我投票赞成结束,因为我相信这个问题在目前的状态不适合这样的格式。一个很长的答案是,每个人的编辑似乎都是错误的。相反,我将把它重新格式化成小的有用的块,就像他们处理这个问题时所做的那样(对程序员来说,这是最乐观的问题)。
- 真的。冗长的答案,几十张赞成票,但没有人提到通过HTTP提供登录表单的常见错误。我甚至和那些说"但是它提交到https://…"的人争论过,当我问他们是否确定攻击者没有重写表单上的非加密页面时,他们只是茫然地盯着我。
- good point@dzuelke,更不用说用户没有直接的方法来检查其敏感数据是否会通过安全连接传输到值得信赖的服务器(我的意思是,检查服务器证书)。
- github.com/fallibleinc/security-guide-for-developers是一个很好的参考
- 建议将问题移动到so documentation meta.stackoverflow.com/questions/332092/…
- 用时间怎么办?填写表格需要时间吗?一个人应该做一些研究,向肉之类的机器人提供一个网站,观察填写表单和提交所需的时间。应该在一秒钟以下。然后观察用户需要什么时间。简单地取平均值,或者甚至做一个贝叶斯分类来区分一个和另一个…
- @有一段时间,人们可以判断一个用户是否是一个机器人,因为当移动到点击一个Enter按钮时,一个机器人会直接向下,然后直接穿过。当然,人类以奇怪的伪随机对角线移动。所以机器人程序编写者的反应是让他们的机器人在奇怪的伪随机对角线中移动。如果你能对你的网站进行编程,让其行为符合预期,那么有人可以对他们的机器人进行编程,使其行为符合预期;这只是一场军备竞赛。对计算机来说,依靠那些可以证明是不可行的东西要好得多。
- @我明白。但这意味着,有几个机器人程序编写者不努力。时间是特别的。因为bot编写器需要构建时间循环。这意味着机器人作者需要更多的时间来运行他们的机器人。在某些情况下,这会破坏他们的商业模式。
- @Chris第六部分和第七部分的答案是地址限制,所以时间会受到影响。我的观点是,计算一个人类可能无法打败的数字违背了安全的基本原则之一,也就是说,你不应该试图"胜过"一个机器人。的确,如果你足够聪明,你可以打败大多数机器人,但是参与这样的军备竞赛通常意味着你在其他地方有一个基本的缺陷。消除这个缺陷比保持领先要好得多,因为机器人迟早会超过你,他们只需要做一次。
- 虽然阅读很有趣,但主题确实太宽泛了。安全是一种猫捉老鼠的游戏。黑客们总是会发现新的漏洞,新的安全性会被堵塞,反之亦然。答案中还没有提到:行为检查。例如,谷歌会时不时地提示你输入密码,特别是当你做了意想不到的事情时。所以,如果你在一个你以前从未见过的商店或城镇付款,请少联系银行卡。
第一部分:如何登录
我们假设您已经知道如何构建一个login+password html表单,该表单将值发布到服务器端的一个脚本以进行身份验证。下面的部分将处理健全的实际授权模式,以及如何避免最常见的安全陷阱。好的。
去HTTPS还是不去HTTPS?好的。
除非连接已经是安全的(即,通过使用ssl/tls的https进行隧道连接),否则您的登录表单值将以明文形式发送,允许在浏览器和Web服务器之间的行上窃听的任何人都可以在登录通过时读取登录信息。这种窃听通常是由政府进行的,但一般来说,我们不会处理"拥有"的电线,除非这样说:如果您要保护任何重要的东西,请使用https。好的。
本质上,在登录期间防止窃听/数据包嗅探的唯一实用方法是使用HTTPS或其他基于证书的加密方案(例如,TLS)或经验证和测试的质询响应方案(例如,基于Diffie-Hellman的SRP)。任何其他方法都很容易被窃听攻击者规避。好的。
当然,如果你愿意变得有点不切实际,你也可以使用某种形式的双因素认证方案(例如,谷歌认证应用程序、一个物理的"冷战式"代码本或RSA密钥生成器加密狗)。如果应用正确,即使是在不安全的连接上,这也可以工作,但是很难想象开发人员会愿意实现双因素身份验证,而不是SSL。好的。
(不要)滚动你自己的javascript加密/散列好的。
考虑到在您的网站上设置一个SSL证书的非零成本和感知到的技术困难,一些开发人员倾向于在浏览器中使用散列或加密方案,以避免通过不安全的连接传递明文登录。好的。
虽然这是一个高尚的思想,但它本质上是无用的(并且可能是一个安全缺陷),除非它与上述的思想结合在一起——也就是说,要么用强加密保护线路,要么使用一个经过尝试和测试的挑战响应机制(如果你不知道这是什么,只需知道这是最难证明的,最困难的方法之一T到设计,并且最难在数字安全中实现概念)。好的。
虽然散列密码确实可以有效地防止密码泄露,但它很容易受到重播攻击、中间人攻击/劫持(如果攻击者可以在不安全的HTML页面到达您的浏览器之前向其注入几个字节,他们可以简单地在javascript中注释散列),或暴力攻击。(因为您将用户名、salt和哈希密码都交给攻击者)。好的。
反人类船长好的。
是用来验证码是一类特异性thwart /蛮力字典攻击:自动试错和没有人类操作员。有没有怀疑这是一个真正的威胁,然而,当你处理它的方式seamlessly不需要验证码,登录在服务器端的具体设计方案-内节流式我们讨论这些以后。
验证码的实现是不知道谁创造了你;他们不是人力可解,大多数人是真的ineffective打击僵尸,他们都是对第三世界ineffective廉价的实验室(根据OWASP的血汗工厂,目前的测试通过率是12 500美元),和一些实现的技术在一些可能是非法的湖国家(OWASP认证作弊表)。如果你必须使用一个验证码,验证码的使用谷歌,因为它是硬的OCR定义(因为它已经使用OCR错误图书扫描)和强硬的和用户友好的。
就我个人而言,我没有找到,他们只使用,和使用作为最后当一用户登录失败安切洛蒂作为内节流式是maxed时报和延迟退房。这将是可接受的清洁很少发生,它strengthens漫游系统作为一个整体。
保密码登录/核查
这可能是普通的知识,毕竟我们的注册用户数据泄密、黑客和publicized我们见过在最近几年,但它是说:不要在你的数据库中存储的密码cleartext。用户数据库是routinely泄露或gleaned通黑,SQL注入,如果你储存的原始文件,这是密码,登录即时游戏为您的安全。
所以如果你不能存储的密码,它是你如何检查登录密码登录+结合的形式张贴在正确的是?答案是使用密钥的散列函数的推导。每当一个新的用户创建的密码,或是把你的密码和运行它通过KDF,如argon2,bcrypt,scrypt或pbkdf2,转折的cleartext密码("correcthorsebatterystaple")到一个长期的前瞻性,随机字符串,这是很多更安全的存储在你的数据库。验证登录到相同的哈希函数,你在运行时传递进来的密码,这是造成在盐和比较哈希值存储在字符串到您的数据库。argon2 bcrypt和盐,scrypt商店已经在哈希。在这篇文章中sec.stackexchange退房的更多详细信息。
原因一是盐是用散列本身是不足够的-你想添加一个所谓的"盐",保护对彩虹哈希表。a盐有效地防止密码完全匹配从两个相同的哈希值被存储为防止被扫描整个数据库,运行在一个四attacker一直就这样在事后来劝告正在执行一个密码攻击。
密码存储不应使用加密散列,因为用户选择的密码不够强(即通常不包含足够的熵),具有散列访问权限的攻击者可以在相对短的时间内完成密码猜测攻击。这就是使用kdf的原因——这些有效地"拉伸密钥",这意味着攻击者所做的每一个密码猜测都会导致哈希算法重复多次,例如10000次,这会导致攻击者猜测密码的速度减慢10000倍。好的。
会话数据-"您以spiderman69登录"好的。
一旦服务器根据用户数据库验证了登录名和密码并找到匹配项,系统就需要一种方法来记住浏览器已经过身份验证。这个事实应该只存储在会话数据的服务器端。好的。
If you are unfamiliar with session data, here's how it works: A single randomly-generated string is stored in an expiring cookie and used to reference a collection of data - the session data - which is stored on the server. If you are using an MVC framework, this is undoubtedly handled already.
Ok.
如果可能,确保会话cookie在发送到浏览器时设置了安全和仅HTTP标志。httponly标志提供了一些保护,防止通过XSS攻击读取cookie。安全标志确保cookie只能通过https发送回,因此可以防止网络嗅探攻击。cookie的值不应是可预测的。如果出现引用不存在会话的cookie,则应立即替换其值以防止会话固定。好的。第二部分:如何保持登录-臭名昭著的"记住我"复选框
持久登录cookie("记住我"功能)是一个危险区域;一方面,当用户了解如何处理它们时,它们与传统登录完全一样安全;另一方面,在粗心的用户手中,它们是一个巨大的安全风险,粗心的用户可能会在公共计算机上使用它们,而忘记注销,他们可能不会现在,什么是浏览器cookie,或者如何删除它们。好的。
就我个人而言,我喜欢经常访问的网站的持续登录,但我知道如何安全地处理它们。如果您确信您的用户也知道这一点,那么您就可以用良心清白的持久登录。如果不是,那么你可以接受这样一种理念:如果用户不小心使用了他们的登录凭证,那么一旦他们被黑客攻击,他们就会受到攻击。这也不像我们去用户的家里,撕掉所有的facepalm诱导贴,上面写着他们在显示器边缘排列的密码。好的。
当然,有些系统负担不起任何帐户被黑客攻击;对于此类系统,您无法证明持久登录是正确的。好的。
如果您确实决定实现持久登录cookie,那么可以这样做:好的。
首先,花些时间阅读Paragon Initiative关于这个主题的文章。你需要把一堆元素弄对,这篇文章对每一个都做了很好的解释。好的。
为了重申最常见的陷阱之一,不要将持久登录cookie(token)存储在数据库中,只存储其中的一个散列值!登录令牌相当于密码,因此如果攻击者掌握了您的数据库,他们可以使用令牌登录到任何帐户,就像他们是明文登录密码组合一样。因此,在存储持久登录令牌时,请使用散列(根据https://security.stackexchange.com/a/63438/5002,弱散列就可以了)。好的。
第三部分:使用秘密问题
不要执行"秘密问题"。"秘密问题"功能是一种安全反模式。从"必须读"列表的链接4中读取纸张。你可以问莎拉佩林关于那个,在她的雅虎之后!在前一次总统竞选中,电子邮件帐户遭到黑客攻击,因为她对安全问题的回答是…"瓦西拉高中"!好的。
即使有用户指定的问题,大多数用户也很可能选择:好的。
一个"标准"的秘密问题,比如母亲的娘家姓或最喜欢的宠物。好的。
任何人都可以从他们的博客、LinkedIn个人资料或类似资料中提取到一个简单的琐事。好的。
任何比猜测密码更容易回答的问题。对于任何一个像样的密码来说,这是你能想象到的每一个问题好的。
总之,安全问题本质上是不安全的,几乎在所有形式和变化中都是不安全的,不应以任何理由将其用于身份验证方案。好的。
安全问题甚至存在于野外的真正原因是,它们方便地节省了一些无法访问电子邮件以访问重新激活代码的用户的支持呼叫成本。这是以牺牲安全和莎拉·佩林的名誉为代价的。值得的?大概不会。好的。第四部分:忘记密码功能
我已经提到了为什么你不应该使用安全问题来处理忘记/丢失的用户密码;也没有说你不应该向用户发送他们的实际密码。在这个领域,至少还有两个太常见的陷阱需要避免:好的。
不要将忘记的密码重置为自动生成的强密码-这种密码众所周知很难记住,这意味着用户必须更改或写下来-例如,在显示器边缘的亮黄色贴纸上。不要设置新密码,只需让用户立即选择一个新密码-这是他们无论如何都想做的。(这可能是一个例外,如果用户普遍使用密码管理器来存储/管理通常不写下来就无法记住的密码)。好的。
总是散列数据库中丢失的密码代码/令牌。同样,此代码是另一个等价密码的示例,因此必须对其进行哈希处理,以防攻击者接触到您的数据库。当请求丢失的密码时,将明文代码发送到用户的电子邮件地址,然后散列它,将散列保存在数据库中——并丢弃原始密码。就像密码或持久登录令牌。好的。
最后一点注意:始终确保用于输入"丢失密码代码"的界面至少与登录表单本身一样安全,否则攻击者只需使用该界面获取访问权。确保生成非常长的"丢失密码"(例如,16个区分大小写的字母数字字符)是一个很好的开始,但是考虑添加与登录表单本身相同的限制方案。好的。第五部分:密码强度检查
首先,您需要阅读这篇小文章来进行实际检查:500个最常见的密码好的。
好吧,所以这个列表可能不是任何系统中最常见密码的规范列表,但它很好地说明了当没有强制执行的策略时,人们选择密码的能力有多差。另外,当你将它与最近被盗密码的公开分析相比较时,这个列表看起来非常接近。好的。
所以:由于没有最低的密码强度要求,2%的用户使用前20个最常见的密码之一。意思是:如果攻击者只有20次尝试,你网站上的50个账户中就有1个会被破解。好的。
要阻止这种情况,需要计算密码的熵,然后应用阈值。国家标准与技术研究所(NIST)特别出版物800-63有一套非常好的建议。当结合字典和键盘布局分析(例如,"qwertyuiop"是一个错误的密码)时,可以在18位熵的水平上拒绝99%的错误选择的密码。简单计算密码强度并向用户显示视觉强度计是好的,但不够。除非强制执行,否则很多用户很可能会忽略它。好的。
为了刷新高熵密码的用户友好性,强烈推荐Randall Munroe的密码强度xkcd。好的。第六部分:更多-或:防止快速登录尝试
首先,看看这些数字:密码恢复速度-密码的有效期有多长好的。
如果您没有时间浏览该链接中的表,下面是它们的列表:好的。
破解一个弱密码几乎不需要时间,即使你是用算盘破解它。好的。
如果不区分大小写,则几乎不需要花费时间破解字母数字9字符密码。好的。
如果密码长度小于8个字符,几乎不需要花时间破解复杂的符号、字母和数字、大小写密码(台式电脑可以在几天甚至几小时内搜索7个字符)。好的。
但是,如果您限制为每秒尝试一次,那么破解一个6个字符的密码需要花费大量的时间!好的。
那么我们能从这些数字中学到什么呢?好吧,很多,但我们可以把重点放在最重要的部分:防止大量快速启动的连续登录尝试(即蛮力攻击)并没有那么困难。但是,正确地预防并不像看上去那么容易。好的。
一般来说,您有三种选择都能有效地抵御蛮力攻击(和字典攻击,但由于您已经采用了强密码策略,因此它们不应该是问题):好的。
在N次失败的尝试之后出示一个验证码(令人讨厌,而且通常无效——但我在这里重复我自己)好的。
尝试n次失败后锁定帐户并要求电子邮件验证(这是等待发生的DoS攻击)好的。
最后,登录限制:也就是说,在n次失败的尝试之后设置尝试之间的时间延迟(是的,DoS攻击仍然可能,但至少它们的可能性要小得多,而且实现起来要复杂得多)。好的。
最佳实践1:随着失败尝试次数的增加而增加的短时间延迟,例如:好的。
- 1次尝试失败=无延迟
- 2次失败的尝试=2秒延迟
- 3次失败的尝试=4秒延迟
- 4次失败的尝试=8秒延迟
- 5次失败的尝试=16秒延迟
- 等。
DOS攻击这个方案是非常不切实际的,因为产生的锁定时间略大于前一个锁定时间的总和。好的。
To clarify: The delay is not a delay before returning the response to the browser. It is more like a timeout or refractory period during which login attempts to a specific account or from a specific IP address will not be accepted or evaluated at all. That is, correct credentials will not return in a successful login, and incorrect credentials will not trigger a delay increase.
Ok.
最佳实践2:在n次失败尝试后生效的中等长度时间延迟,例如:好的。
- 1-4次失败的尝试=无延迟
- 5次失败的尝试=15-30分钟延迟
DOS攻击这个计划是非常不切实际的,但肯定是可行的。另外,值得注意的是,这种长时间的延迟对于合法用户来说是非常烦人的。健忘的用户会不喜欢你。好的。
最佳实践3:将这两种方法结合在一起-在n次失败尝试后,固定的短时间延迟生效,例如:好的。
- 1-4次失败的尝试=无延迟
- 5次以上失败的尝试=20秒延迟
或者,具有固定上限的增加延迟,例如:好的。
- 1次失败的尝试=5秒延迟
- 2次失败的尝试=15秒延迟
- 3次以上失败的尝试=45秒延迟
该最终方案取自OWASP最佳实践建议(必须阅读的列表中的链接1),应被视为最佳实践,即使该方案确实存在限制性。好的。
As a rule of thumb, however, I would say: the stronger your password policy is, the less you have to bug users with delays. If you require strong (case-sensitive alphanumerics + required numbers and symbols) 9+ character passwords, you could give the users 2-4 non-delayed password attempts before activating the throttling.
Ok.
DOS攻击这个最终登录限制方案是非常不切实际的。最后一点是,始终允许持久(cookie)登录(和/或验证了验证码的登录表单)通过,这样合法用户在攻击过程中甚至不会被延迟。这样,非常不切实际的DoS攻击就变成了非常不切实际的攻击。好的。
此外,对管理帐户进行更积极的限制是有意义的,因为这些是最有吸引力的入口点好的。第七部分:分布式暴力攻击
和旁白一样,更高级的攻击者会试图通过"散布他们的活动"来绕过登录限制:好的。
在这里,最佳实践是记录失败登录的数量、系统范围,并使用站点错误登录频率的运行平均值作为对所有用户施加的上限的基础。好的。
过于抽象?让我换个说法:好的。
假设你的网站在过去的3个月里平均每天有120次错误登录。使用这个(运行平均值),您的系统可能会将全局限制设置为3倍,即24小时内360次失败的尝试。然后,如果所有帐户的失败尝试总数在一天内超过该数字(或者更好的方法是,监视加速率并在计算的阈值上触发),它将激活系统范围内的登录限制-这意味着所有用户的延迟都很短(但cookie登录和/或备份captcha登录除外)。好的。
我还发布了一个更详细的问题,并讨论了如何避免分布式蛮力攻击中的棘手陷阱。好的。第八部分:双因素认证和认证提供者
无论是通过漏洞攻击、密码被写下来并丢失、带有密钥的笔记本电脑被偷,还是用户登录到网络钓鱼网站,都可能破坏凭据。使用双因素认证可以进一步保护登录,该认证使用带外因素,例如从电话呼叫、短信、应用程序或加密狗接收到的一次性代码。一些提供者提供双因素认证服务。好的。
身份验证可以完全委托给一个登录服务,在该服务中,另一个提供者负责收集凭证。这就把问题推给了一个可信的第三方。谷歌和Twitter都提供基于标准的SSO服务,而Facebook也提供类似的专有解决方案。好的。有关Web身份验证的必读链接
OWASP认证指南/OWASP认证备忘表
网上客户认证的注意事项(麻省理工学院的研究论文非常易读)
维基百科:HTTP cookie
回退认证的个人知识问题:Facebook时代的安全问题(非常易读的伯克利研究论文)
好啊。
- 好吧,我不太同意Captcha的说法,是的,Captcha很烦人,它们可能会被破坏(除了Recaptcha,但人类几乎无法解决这个问题!)但这就像是说不要使用垃圾邮件过滤器,因为它有少于0.1%的误报。这个网站使用captchas,它们并不完美,但是它们减少了大量的垃圾邮件,而且根本没有好的替代方案。
- @杰夫:听到你对我的答复有异议,我很难过。我不知道关于这个答案有什么争论,如果你让我编辑的话,我会很乐意自己编辑的。删除我的帖子刚从我的帐户中删除了1200个信誉,这很伤人:(
- 很好的回答,谢谢。您能解释为什么持续登录的"改进"最佳实践存在缺陷吗?乍一看,它确实解决了用Miller方法使会话无效的DOS场景,我遗漏了什么?
- 别忘了看看你的加密算法。你不想被侧道攻击,比如定时攻击。
- "发送身份验证令牌之后,系统需要一种方法来记住您已经过身份验证——这个事实应该只存储在会话数据的服务器端。cookie可以用来引用会话数据。您可以(而且应该,对于无状态服务器!)使用加密签名的cookie。这是不可能伪造的,不捆绑服务器资源,不需要粘性会话或其他恶作剧。
- 是否有好文章讨论(1)挑战应对机制背后的理论和/或(2)实施?
- 用于永久登录。用户名+令牌如何优于服务器映射到用户名和登录状态的随机会话ID?我以前从未听说过第一种方法。
- 这是我在堆栈溢出中看到的最长和最有趣的答案。但它确实更适合写博客!
- 对于持久登录,第二个cookie如何优于一个到期时间较长的cookie/会话?
- 虽然TylerAtkin的密码强度库看起来很好,但它是在GPL下获得许可的,这对于一个JavaScript库来说有点奇怪。在商业Web应用程序中提供一个可以不受限制地使用的替代实现的链接是很好的。
- "台式电脑可以在不到90天的时间内搜索最多7个字符的完整密钥空间"最近使用GPU的机器可以在不到1天的时间内搜索最多7个字符的完整密钥空间。一个顶级的GPU每秒可以管理10亿个哈希。golubev.com/hashgpu.htm这导致了一些关于密码存储的结论,这些结论并没有直接解决。
- 我完全同意你关于安全问题的看法。他们很坏。我只是希望我能向我的银行解释一下。
- openid被排除在外。有没有可能有人可以添加它?
- @Aero:通过将用户名包含在持久登录cookie令牌中,可以避免攻击者有1000万次猜测正确令牌的机会(在拥有1000万用户的站点上)。当然,如果令牌字符串足够长,它不应该是一个大问题,但是我仍然推荐它。
- @Sidnicious:会话cookie根据定义是暂时的,即当用户关闭浏览器窗口时,它们会自动过期。
- @科林·伯恩:你知道远程网站有没有成功的定时攻击?从我读过的有关成功定时攻击的文献来看,它们需要亚毫秒的方差,这使得它们不适用于远程攻击的网站(通常的响应时间在20-50毫秒范围内)。
- @当然-我在文章中添加了一个简单的注释,解释了为什么它没有为系统增加重要的安全性。
- Jens为什么?如果cookie的目的是存储有关登录会话的信息,为什么它不能持续到登录持续的时间?似乎有单独的会话/记住我饼干只是增加了复杂性。
- @西德尼西亚:那你不是在说会话cookie。根据定义,会话cookie没有Expires指令。如果向会话cookie添加expires指令,它将成为持久cookie。听起来你想让你的"会话"cookie持久化,并消除真正的会话限制cookie。我想这是可能的,但是你失去了所有可以用会话cookie做的有用的事情,即在内存中保留暂时的轻量级状态对象(相信我,你不想让所有会话数据保持几个月的活动状态)。
- 我很惊讶没有提到CSRF保护…
- @詹斯罗兰:关于记忆我,在cookie中,我应该保存存储在数据库中的相同令牌?如果不是,我应该在cookie中保存发起令牌的值?
- @乔纳森:令牌只是一个随机生成的值。将用户ID和令牌保存在cookie中,然后将令牌的散列保存在数据库中。这样,您就可以通过重新散列令牌并检查用户记录是否匹配来验证登录cookie。
- @迈克:不错。如果用户试图登录(即向服务器发布登录凭据),则不会检查这些凭据,并返回错误。服务器同步倒计时时钟应显示在登录表单旁边,以通知用户节流。在倒计时完成之前,应禁用表单本身或"提交"按钮。
- @Mike:基于账户的"锁定"应该只在用户在登录时违反了您网站的TOS、未支付账户费用或用户被当局调查的情况下发生。在用户正确登录之前,您(通常)不能确定失败的登录尝试不是恶意的第三方试图锁定真实用户。
- @迈克:但是,基于IP的"锁定"是明智的,以防明显的DoS攻击。但是,这本身与登录表单的安全性没有任何关系,通常直接在网络流量上处理,因此来自单个IP的请求溢出会自动触发一个IP块数小时。
- @Jens,谢谢你的澄清。我只是在想我在两个不同的银行有账户。其中一个会将您的帐户锁定2小时,登录3次失败。另一个在登录失败5分钟后无限期地锁定您的帐户,并强制您致电他们的技术支持以重新启用它。两个(据我所知)都不锁定你的IP地址。
- @jfager:当用户重新登录时,不会删除任何内容,因为只有在向站点传递包含有效序列标识符和无效令牌的旧cookie时,才会发生安全删除。由于没有这样的cookie(因为攻击者删除了它),系统只启动一个新的系列,甚至不警告用户。(请记住:一个用户可以拥有许多笔记本电脑=许多系列标识符,而"改进"的系统无法区分这些区别)
- @JFager:完全正确——我不是有意把它描述成一个缺点,我唯一的观点是"改进"并没有实现它的承诺。它可以使实现者和站点所有者相信他们的认证系统可以防止cookie窃取,但事实并非如此——而且伪造的安全性比没有安全性更糟糕。
- 这里有几件事,要么简单地提到,要么根本不提到。一种是使用特定于密码的哈希算法(例如bcrypt)。这意味着计算散列的时间会增加到0.1秒(至少),但在搜索整个键空间时,这会产生很大的差异。第二件事是,安全性必须无处不在:对用户输入进行卫生处理,为SQL使用准备好的语句,确保资源不可预测URL,然后就出现了整个XSS集的问题…
- 同意@chacham15。锁定和限制策略很好,但是使用密码摘要(如bcrypt或scrypt)而不是通用的哈希函数可以在尽可能低的级别强制执行此操作。不可能为了生成密码摘要而投入全部的工作,因此任何开发人员都不能简单地忘记实现或意外地禁用限制机制。
- @更正:即使所有用户都有高熵密码,我们仍然希望在数据库被盗的情况下对其进行哈希处理。
- @费迪:谢谢:)一个固定的1或2秒的延迟仍然有效,但是将其增量增加到10或20秒将增加抵御蛮力攻击的弹性。
- @Jensroland感谢您确认简化模型工作。尽管它效率较低,但我还是希望避免可靠跟踪登录尝试的痛苦。更好的是,我最喜欢你答案的第八部分:将问题一起外包。在构建自己的用户管理系统的过程中,不仅仅是安全问题,看起来简单而可靠地发送电子邮件也是一个难题。
- 如果你想保留任何一位客户夫人,永远不要使用recaptcha。说真的,如果你必须使用一个验证码,然后使用一个人类有机会解决第一次,他们可能不是牢不可破,但他们是一个很好的折衷,因为大多数将停止各种机器人程序,而不会让你的客户太过恼火。
- @Mikemike:"…并在PHP中循环它们"——为什么不在SQL中选择行呢?SELECT * FROM LoginTokens WHERE UserID=[userid from cookie] AND HashedToken=[hash(token from cookie)]应该工作得很好(不过,记住要为SQL使用准备好的语句/存储过程)
- 有人知道我在哪里可以找到查理斯·米勒(CharlesMiller)有将近10年历史的"持久登录cookie"解决方案的经过审查的php/mysql实现吗?请在这里发布:stackoverflow.com/questions/15647261/…
- @Jensroland:当用bcrypt散列令牌时,如何选择SQL中的行?请回答:stackoverflow.com/questions/15685951/…
- @杰西:如果"改进的"系统真的做到了这一点,我同意你的看法,但是如果攻击者删除了cookie,那么该站点就不知道发生了攻击——只是在有效链集合中添加了新的链。如果并且只有当用户监控自己数据库中存储的有效链的数量,从不清除自己的cookie,并跟踪他用于在站点上进行身份验证的100%设备时,他才能推断出可能发生了攻击。系统自动检测入侵的场景不适用于cookie删除。
- @杰西:你是对的,但是我觉得改进是非常投机的,因为基本上每一个允许第三方访问cookie的攻击向量都会让他们删除cookie。希望他们不会这样做,只不过是通过默默无闻来保证安全。即使对最弱的攻击者提供了额外的保护,我仍然认为,通过广告宣传一种"入侵检测"机制,给网站所有者/合法用户一种虚假的安全感,这种机制实际上并不保护他们,是完全不负责任的,应该予以劝阻。
- 只是一个想法。像比特币这样的客户端凭证散列怎么办?用户名、密码和随机数(特定于会话)的迭代散列在客户机上需要几秒钟(合法用户不介意稍有延迟),这对攻击者来说是一场噩梦。成功的身份验证需要有效的凭证和工作证明。
- "散列密码客户端":这不仅仅是说这是"无用的",应该非常清楚这是一个安全缺陷。这使得散列密码等效,意味着服务器通过存储密码散列,有效地存储了密码(我们都没有这样做,对吗?)这里有一个关于这一点的问题。
- @Jensroland Great Post,谢谢。例如,如果我的站点没有它想在会话cookie中保留的任何状态,那么我的会话cookie和持久cookie基本上都只包含不同的大随机字符串(以及持久的用户名)吗?
- @newtang:持久cookie应该始终是一个大的随机字符串(为了方便起见,还可以选择用户名)。会话cookie通常由web/mvc框架(asp.net_sessionid、phpsessid、ci_session、jsessionid)设置,并且应该只包含会话标识符(另一个较大的随机字符串)。如果您想保持关于会话的其他状态,那么该状态通常应该存储在服务器端,会话标识符用作主键。
- @香农:当然可以——只要你的用户数据受到强加密的保护,理论上你可以把它上传到Pastebin,这不会是个问题——但是我认为没有人会对这种设置感到满意。一些框架实际上提供了这样一个加密的cookie解决方案,作为会话数据库的替代方案,毫无疑问,这是一个可行的解决方案。当人们使用弱加密或不加密(他们这样做),或者超过cookie数据的数据限制来实现该解决方案时,问题就开始了。
- @Jensroland:我想我的想法是,如果我们假设加密很弱,那么我们必须担心即使是一个简单的会话令牌也是不安全的,我们会冒着会话劫持、重播和任何其他相关攻击的风险。
- 当与典型的密码重置功能结合使用时,"秘密问题"是否有益?典型的密码重置过程确保只有电子邮件帐户的所有者才能重置其密码。但是,如果用户的电子邮件帐户受到威胁,那么添加安全问题可能会阻止重新设置密码和访问网站的尝试。
- 我看到一个用CanvasHTML5做的验证码,它移动了三个球,使它们在最小到最大的范围内(圆而不是球),实际上非常好。我有一阵子没看到那一页了。
- @安德鲁:事实并非如此。允许使用多个SID(链),因为这是支持多个登录设备(如家庭PC、工作PC和iPad)的方式。在单链(单设备)方案下,每次用户在设备之间切换时,都会发出"窃取"警告,最终使其变得毫无价值,因为用户总是看到它。
- @Jensroland如果你想让RememberMe用于多个设备,那么你必须修改解决方案。我认为有一种方法可以代替SID,将IP(散列)存储在cookie和db中。数据库将拥有一个IP池,其中一些被标记为有效,另一些被标记为禁止。在手动登录时,用户的IP存储在cookie和数据库中,并标记为有效。每次自动或手动登录时,都会根据DB(令牌)检查传入用户的IP。如果找到IP并且有效-绿灯。如果找不到IP,则绿灯亮,但传入用户的IP标记为有效和
- 旧IP(来自cookie)在数据库中被标记为禁止。如果所有者使用旧的(禁止的)IP登录,则假定为红灯盗窃。一切都被清除了。这不是100%的证据,因为在真正的所有者回来之前,窃贼将使用该站点,而且在这段时间内,所有者的IP可能会发生变化,但机会被最小化,允许使用多个设备。或者,可以每月/3个月删除被禁止的IP,因为ISP可能会再次分配"旧"IP。
- 请注意:SSL不是"安全登录"的唯一解决方案。实际上,您可以使用散列密码使嗅探器安全登录过程。基本上,在生成登录页面时,您向浏览器发送一个salt(它也将保存在服务器端),在提交表单数据时,您将发送哈希密码和salt(look@crypto js),然后服务器对会话中的密码+生成的salt进行哈希,并对两者进行比较。如果服务器上的密码是散列的,只需在浏览器上散列密码,然后再次连接salt和hash。
- @Wolfulus:首先,作为一个MITM攻击者,我要做的就是替换您通过未加密的连接发送给浏览器的salt(或JS哈希函数),而您的整个方案是无效的。其次,即使我不能提前操纵服务器响应,简单的嗅探也会给我salt、散列函数和散列密码+salt,这将为您打开简单的字典和暴力攻击。永远不要为了安全而信任客户端散列。
- @JensRoland salt也存储在服务器端进行验证,如果在客户机上更改它,服务器验证将失败,从而导致密码无效。我同意这种野蛮行为。
- @沃尔夫卢斯:即使登录没有通过,我现在拥有你的密码,尽管你努力用哈希屏蔽它。
- @JameshFisher"不仅仅是说这是"无用的",还应该明确指出这是一个安全缺陷。这使得散列密码等效,意味着服务器通过存储密码散列,有效地存储了密码"如果您不在服务器端使用单独的盐散列,这只是一个安全缺陷。"如果在客户机端简单地散列,然后在服务器端加上盐散列,那么密码本身就不会在传输中,破解数据库仍然不意味着能够进行身份验证。
- @Parthianshot有一点道理,但是客户端散列仍然是无用的——向安全系统添加不必要的复杂性只会创建另一个移动的部分,这可能会混淆维护人员,并可能引入安全缺陷(例如导致缺乏经验的开发人员放弃服务器端salt+重新散列)
- @Jensroland"带领一个缺乏经验的开发人员放弃服务器端salt+rehashing"让我想起了"人们在设计完全不可靠的东西时犯的一个常见错误是低估了完全不可靠的人的创造力"。你需要假设你有一个胜任的开发团队。然而,对哈希客户端进行salt有一个巨大的优势:在事实发生后解密流量的人仍然只能在您的站点上使用他们获得的身份验证令牌,无论最终用户是否在多个站点上使用相同的密码。尽管纵深防御确实增加了复杂性。
- 老实说,我不喜欢查理·米勒的策略。我喜欢的系统只使用两个随机值:一个作为索引存储在数据库中,另一个存储在客户机的cookie中,其散列存储在数据库中。然后我们在恒定时间内比较散列值。paragonie.com/blog/2015/04/…-在此详细解释。
- 我在考虑会话数据:"这个事实应该只存储在会话数据的服务器端"。关于集群环境(比如云环境)?如何存储用户会话?
- @Kavain:在这样的环境中,通常将会话保存在数据库中(托管在单独的服务器上)。这将会话与处理请求的特定Web服务器(无状态HTTP FTW)分离,并且具有与数据库安全性相等的最低安全级别,这通常是一件好事。
- 有趣的是,答案中提到了SRP,但是很多评论都在讨论过时的散列。像thinbus srp这样的javascript srp库速度快、效率高,唯一的开销是获取salt,很少有服务器需要对服务器执行零知识密码验证。
- @Jensroland感谢您的回答!您能详细说明一下吗?"在将响应返回到浏览器之前,延迟不是延迟。这更像是超时…在此期间,登录尝试到特定帐户…根本不会被接受或评估。"—我知道你基本上是说,在发送"无效登录详细信息"响应之前使用类似sleep(30)的方法是不行的,因为bot/hacker可以继续提交登录尝试,同时执行单独的请求;但是你如何建议执行超时?是否禁用窗体的登录按钮?
- @Kostakontos您需要触发一个在服务器端运行的非同步超时,仅此而已。如何向用户显示这些信息只是一个用户界面问题,因为安全措施根本不在前端。
- 好作品。然而,密码强度几乎完全无关——站点之间的唯一性才是最重要的。要么数据库已被捕获,在这种情况下,实际用户数据将丢失。或者攻击者正在尝试蛮力网络攻击-您可以轻松缓解。除非用户的密码非常弱(如monkey123),只要一个密码跨站点是唯一的,那么超级强密码在很大程度上是没有意义的,并且会导致用户忘记它们。如果你特别告诉用户使用唯一的密码,而他们没有,那是他们的责任。
- @如果攻击者希望以特定用户为目标,NIICO密码强度并不重要(在某些系统上,攻击者可能足以访问任意帐户,但在许多情况下,例如在社交网站上,攻击者希望破解特定用户帐户)。破解单个用户密码的难度取决于允许的登录尝试次数,但也很大程度上取决于密码的强度。如果允许人们使用低熵密码,他们将选择"密码"、"秘密"、"QWERTY"和"Letmein"(以及他们的狗的名字)。你不需要太多的猜测。
- 关于"不要将忘记的密码重置为自动生成的强密码-这样的密码很难记住,这意味着用户必须…让用户立刻选择一个新的密码,这是他们无论如何都想做的事情。我会说,如果你使用第三方密码管理器,这(记住自动生成的密码)不再是一个问题。我还假设,让用户选择一个新的密码(如果他们依靠人类记忆来保留密码),他们将选择一个容易破解或重复的密码,不管怎样,10次中有9次。现在你又回到了一个更严重的问题上。
- "当请求丢失密码时,将明文代码发送到用户的电子邮件地址"不应将明文密码存储在数据库中
- @韦伯曼:我觉得你很困惑。这句话继续说:"然后散列它,将散列保存在数据库中——然后扔掉原始的"。它明确表示不要将明文代码存储在数据库中。至于captcha,是的,现在许多实现对于半途而废的模式识别软件都是无效的。
- 好吧,我明白了,如果你能将明文代码发送给用户,这意味着你将它作为明文存储在数据库中,因为afaik不可能逆转哈希过程,但我可能是错的,好吧,对于captcha,我现在明白你的意思了。
- 关键是每次都会生成一个新的随机变量。如果数据库中还有以前的重置代码散列(这些散列应该在有限的时间(比如30分钟)后自动过期),您只需覆盖它。另一种选择是创建一个有效负载,并将其包装成类似于JWT的东西,服务器可以在收到时对其进行验证,但只有当您在其他地方使用JWT时,这才是真正值得的,因此您已经有了适当的逻辑。
最终条款正在发送凭据
唯一可行的100%安全地发送凭证的方法是使用SSL。使用javascript散列密码是不安全的。客户端密码散列的常见缺陷:好的。
- 如果客户机和服务器之间的连接未加密,那么您所做的一切都容易受到中间人攻击。攻击者可以替换传入的javascript来破坏散列或将所有凭证发送到其服务器,他们可以监听客户端响应并完全模拟用户等。具有可信证书颁发机构的SSL旨在防止MITM攻击。
- 如果您不在服务器上做额外的、冗余的工作,服务器接收的哈希密码就不那么安全。
还有另一种称为srp的安全方法,但它已获得专利(尽管它是免费许可的),而且几乎没有好的实现可用。好的。存储口令
不要将密码以明文形式存储在数据库中。即使你不关心你自己网站的安全也不行。假设您的一些用户将重用其在线银行帐户的密码。所以,存储哈希密码,并扔掉原始密码。并确保密码不会出现在访问日志或应用程序日志中。OWASP建议使用argon2作为新应用程序的首选。如果不可用,则应使用pbkdf2或scrypt。最后,如果以上都不可用,请使用bcrypt。好的。
散列本身也是不安全的。例如,相同的密码意味着相同的哈希——这使得哈希查找表成为同时破解大量密码的有效方法。相反,把盐腌的土豆泥储存起来。salt是在散列之前附加到密码的字符串-每个用户使用不同的(随机)salt。salt是一个公共值,因此可以将它们与哈希一起存储在数据库中。更多信息请参见此处。好的。
这意味着您不能向用户发送他们忘记的密码(因为您只有散列值)。除非您已经验证了用户身份,否则不要重置用户的密码(用户必须证明他们能够读取发送到存储(和验证)电子邮件地址的电子邮件。)好的。安全问题
安全问题是不安全的-避免使用它们。为什么?任何安全问题都可以,密码更好。阅读第三部分:使用@jens roland中的秘密问题在此wiki中回答。好的。会话cookie
用户登录后,服务器向用户发送会话cookie。服务器可以从cookie中检索用户名或ID,但没有其他人可以生成这样的cookie(要解释机制)。好的。
cookies可以被劫持:它们只有和客户机的其他机器和其他通信一样安全。它们可以从磁盘读取,在网络流量中嗅探,被跨站点脚本攻击解除,从中毒的DNS进行网络钓鱼,因此客户端将其cookie发送到错误的服务器。不要发送持久的cookie。cookie应该在客户端会话结束时过期(浏览器关闭或离开您的域)。好的。
如果要自动登录用户,可以设置持久cookie,但它应与完整会话cookie不同。您可以设置一个额外的标志,用户已经自动登录,并且需要为敏感操作实际登录。这一点很受购物网站的欢迎,这些网站希望为您提供无缝、个性化的购物体验,但仍然保护您的财务细节。例如,当你返回亚马逊时,他们会给你显示一个看起来你已经登录的页面,但是当你去下订单(或者更改你的送货地址、信用卡等)时,他们会要求你确认密码。好的。
另一方面,银行和信用卡等金融网站只有敏感数据,不应允许自动登录或低安全模式。好的。外部资源列表
- Web上客户端身份验证的注意事项(pdf)21页的学术文章,有很多好的提示。
- 询问YC:用户身份验证的最佳实践论坛主题讨论
- 您可能存储的密码不正确关于存储密码的介绍性文章
- 讨论:编写恐怖代码:您可能错误地存储了密码论坛讨论了一篇恐怖文章的编码。
- 不要将密码存储在数据库中!关于在数据库中存储密码的另一个警告。
- 密码破解维基百科关于几种密码散列方案弱点的文章。
- 彩虹表足够了:关于安全密码方案你需要知道什么讨论彩虹表以及如何防御它们,以及如何抵御其他线程。包括广泛的讨论。
好啊。
- 考虑到最近有关签名的SSL证书的MITM漏洞(blog.startcom.org/?p=145)因此,将ssl和某种挑战响应身份验证(有SRP的替代方案)结合起来可能是更好的解决方案。
- 很多这样的东西是情境性的。我倾向于不使用会话cookie。cookies被劫持几乎总是服务器的故障。中间人/包嗅探不是那么常见
- bcrypt nuget包:nuget.org/list/packages/bcrypt
- 关于这个答案的注1:它是一个草稿,要作为wiki编辑。如果您可以编辑此内容,欢迎访问。
- 如果我理解的很好,SRP特定于多个当事方的存在。
首先,强烈警告说,这个答案不适合这个确切的问题。这绝对不是最好的答案!
我将继续讨论Mozilla提出的browserid(或者更准确地说,是经过验证的电子邮件协议),本着在未来找到更好的身份验证方法的升级路径的精神。
我将这样总结:
Mozilla是一个非营利性组织,它的价值观与找到解决这个问题的好方法很一致。
今天的现实是,大多数网站使用基于表单的身份验证
基于表单的身份验证有一个很大的缺点,这增加了网络钓鱼的风险。要求用户将敏感信息输入由远程实体控制的区域,而不是由其用户代理(浏览器)控制的区域。
由于浏览器是隐式信任的(用户代理的整个思想是代表用户进行操作),因此它们可以帮助改善这种情况。
阻碍进度的主要因素是部署死锁。解决方案必须分解为步骤,这些步骤本身就提供了一些增量收益。
表示内置于互联网基础设施中的身份的最简单的分散方法是域名。
作为表示身份的第二级,每个域管理自己的一组帐户。
形式"account@域"简洁,并由各种协议和URI方案支持。当然,这样的标识符是最普遍公认的电子邮件地址。
电子邮件提供商已经是事实上的主要在线身份提供商。当前密码重置流通常允许您控制一个帐户,如果您可以证明您控制了该帐户的相关电子邮件地址。
已验证的电子邮件协议被提议提供一种基于公钥加密的安全方法,用于简化向域B证明您在域A上拥有帐户的过程。
对于不支持已验证电子邮件协议的浏览器(目前所有浏览器),Mozilla提供了一个填充程序,该填充程序在客户端JavaScript代码中实现了该协议。
对于不支持已验证电子邮件协议的电子邮件服务,该协议允许第三方充当受信任的中介,断言他们已经验证了用户对帐户的所有权。不希望有大量此类第三方;此功能仅用于允许升级路径,电子邮件服务更倾向于自己提供这些断言。
Mozilla提供他们自己的服务,使其像这样一个受信任的第三方。实现经过验证的电子邮件协议的服务提供商(即依赖方)可以选择是否信任Mozilla的断言。Mozilla的服务使用传统的发送带有确认链接的电子邮件的方式来验证用户的帐户所有权。
当然,除了他们可能希望提供的任何其他身份验证方法之外,服务提供者还可以提供此协议作为选项。
这里寻求的一个巨大的用户界面好处是"身份选择器"。当用户访问一个站点并选择进行身份验证时,他们的浏览器会向他们显示一组电子邮件地址("个人"、"工作"、"政治活动"等),这些地址可用于向该站点标识自己。
作为这项工作的一部分,另一个巨大的用户界面好处是帮助浏览器了解更多关于用户会话的信息——他们目前主要是以谁的身份登录的——因此它可能会在浏览器的Chrome中显示这些信息。
由于该系统的分布式特性,它避免了锁定到Facebook、Twitter、Google等主要网站。任何个人都可以拥有自己的域,因此可以充当自己的身份提供者。
这并不是严格的"基于表单的网站认证"。但是,这是一项努力,从当前基于表单的身份验证的规范过渡到更安全的规范:浏览器支持的身份验证。
- 浏览链接已断开
- 这个项目似乎已被搁置了……参见en.wikipedia.org/wiki/mozilla_persona
我只是觉得我会分享这个解决方案,我发现它工作得很好。
我称之为虚拟场(尽管我没有发明这个,所以不要相信我)。
简而言之:您只需将它插入到您的