在 Web 应用程序中更改密码时,是否应该注销所有其他会话?

信息安全 密码 验证 会话管理
2021-08-26 03:57:06

当用户在多个选项卡中登录基于 Web 的应用程序并更改密码时,是否应该自动将其从其他选项卡中注销?用例是一个帐户被盗用,攻击者登录,用户也登录并更改他们的密码,但是,由于攻击者已经登录,他们仍然被授予访问权限,直到他们注销。

这似乎需要在会话中存储密码 bcrypt 哈希,然后每个请求都会根据数据库中的 bcrypt 哈希检查会话 bcrypt 哈希。有替代方法吗?

4个回答

TL;DR:你怎么知道不是攻击者在更改密码?在这种情况下,您将注销合法用户。或者用户可能希望定期更改他们的密码(一个很好的做法),但如果他们每次都必须在所有设备上重新登录,则不需要。我只会在以下情况下注销所有其他会话:(1)用户单击“注销所有其他会话”按钮,或(2)用户使用忘记密码功能(从而确认他们的电子邮件地址,或其他提供更强证明是他们而不是密码)。


何时注销所有其他会话?

有几种情况会更改用户的密码:

  • 用户注意到或怀疑存在泄露,并更改了密码
  • 攻击者刚刚获得了对帐户的访问权限并更改了用户的密码
  • 用户忘记密码
  • 攻击者入侵了用户的电子邮件帐户并使用忘记密码功能
  • 用户经常更改他们的密码,要么是因为他们认为它可以提高安全性,要么是因为政策

在每种情况下,您都希望做出不同的选择,但无法区分合法用户和攻击者。由于您在使用忘记密码功能时很难要求用户确认他们的密码,因此我们必须要么忽略攻击者破坏用户电子邮件帐户的情况,要么使用其他确认用户身份的方法(例如最近购买, SMS 验证码、安全问题[1] 等)。

由于从界面更改密码时我们不知道是谁在更改密码,因此我认为让其他会话保持活动状态并非不合理。在这种情况下,其他会话应该在用户重置密码时终止(他们确认密码重置链接之后,而不是在任何人将他们的电子邮件输入表单时,因为这可能会被滥用)。想象一下双方都登录并知道密码的场景:双方都将尝试更改密码(假设他们彼此认识)并且谁先到那里只是运气。但是一旦有人成功使用了密码重置功能,您就更有信心认为这是合法用户。

另一个考虑因素是是否有一个单独的界面来注销其他会话:您可以有一个“注销所有其他会话”按钮,可能在所有其他会话和过去登录的列表旁边。我建议使用这个,因为当他们的帐户发生奇怪的事情时,用户可以检查发生了什么。攻击者将无法隐藏他们的存在。

如何管理会话?

理想情况下,您应该拥有任何帐户的活动会话列表以及登录历史记录。登录历史应该可以追溯到一定的时间,而不是一定的登录次数:如果只显示最后 X 次登录,攻击者肯定会查看帐户历史记录,欺骗用户代理和国家/州/ city 使用 VPN,并进行 X 次登录以将自己的登录名挤出内存。

这可以在数据库中进行管理,其中注册了每个登录会话。使用expires列,您可以清除已过期的会话。当然,会话应该在注销时被删除。请注意,这与登录日志是分开的:注销后,仍然应该可以看到有人<datetime>上使用<location>的<device>登录了该帐户。唯一要删除的(或要切换的布尔值)是会话处于活动状态。

如问题中所述,不需要使用 bcrypt-hashed 密码进行一些技巧(绝对不推荐!)。会话状态在设计上应该独立于用户的密码:否则会话令牌的含义可能会在您犯一些小错误时被猜到,即使攻击者并不真正知道密码。或者相反,攻击者可以通过离线暴力破解密码,而他们所获得的只是一个会话令牌。

一般来说,应该有一个至少包含两列的会话列表:会话令牌和用户的唯一标识符。会话令牌应该是随机字节,可以选择散列(为了不从受损或弱 PRNG 泄漏数据)。用户的唯一标识符应该是稳定的:没有显示名称,最好不是用户名。使用一次性唯一标识符,例如 MariaDB/MySQL 中的自动递增列。通常会添加第三列,即过期时间,但这并不是绝对必要的。不过,我强烈推荐它,即使您将其设置为多年。

对于每个请求,可以检查数据库是否存在会话令牌。如果用户做了一些使其他会话无效的事情,那么停用(或删除)用户的其他会话是一件简单的事情。


[1] 不要使用安全问题来代替密码。我敢提它的唯一原因是因为它可以是一个附加:当用户确认发送到他们的电子邮件帐户的密码(或链接)时,或者当他们证明他们知道他们的密码时,你可以使用它来获取更确定它是真正的用户而不是攻击者。

当用户在多个选项卡中登录基于 Web 的应用程序并更改其密码时,是否应该自动将其从其他选项卡中注销?

如果用户使用基于 cookie 的机制登录,则退出一个选项卡将使用户退出所有选项卡。如果他们只更改密码,那么由于他们仍将在当前选项卡上登录会话,因此他们将在其他选项卡上保持登录状态。除非您已经实现了诸如 Gmail 的多会话功能之类的奇特功能,否则同一浏览器上的每个选项卡都是同一个会话。

用例是一个帐户被盗用,攻击者登录,用户也登录并更改他们的密码,但是,由于攻击者已经登录,他们仍然被授予访问权限,直到他们注销。

是的,这可能发生在某些系统上,例如 ASP.NET 中的表单身份验证。

默认的 Forms Authentication SignOut方法不会更新任何服务器端,允许继续使用捕获的身份验证令牌或更改密码的会话将继续有效:-

调用 SignOut 方法只会删除表单身份验证 cookie。Web 服务器不存储有效和过期的身份验证票证以供以后比较。如果恶意用户获得有效的表单身份验证 cookie,这会使您的站点容易受到重放攻击。


这似乎需要在会话中存储密码 bcrypt 哈希,然后每个请求都会根据数据库中的 bcrypt 哈希检查会话 bcrypt 哈希。有替代方法吗?

是的,您可以跟踪会话服务器端。例如,将加密安全字符串分配为会话 ID,会话 ID 设置为 cookie 值,并在数据库中针对每个请求进行查找。

另一种方法是将密码上次更改日期/时间的哈希值存储为 auth cookie 的一部分,该 cookie 会在每个请求上进行检查。

显然,与可以独立于任何数据库连接检查的 Session ID 相比,这些值具有性能损失,但只要它们适当地过期,就可以缓存这些值。如果会话 ID 存储在服务器端,如果需要滑动到期,您还必须具有适当的机制(同样,有开销)来保持会话活动。

当用户注销或更改他们的密码时,它应该撤销他们的会话令牌,这通常是一个标识他们的哈希,比如 fc2904-92385-4jf9

服务器应该知道特定的会话令牌不再有效,因此如果您在其他窗口中打开了应用程序,并且您尝试继续浏览,服务器应该由于令牌过期而拒绝您的所有请求。

用户更改密码的原因多种多样,不仅仅是因为他的帐户被盗用了。因此,从用户体验的角度来看,在更改密码后随后终止其他打开的会话是很糟糕的。

然而,为了在这种特定情况下实现完美的用户保护,同时保持用户的便利性,更好的方法是在用户在当前会话中执行任何下一步操作之前提示用户输入密码。