如何安全地实现“记住我”功能?

信息安全 验证 Web应用程序 php 应用安全 截止日期
2021-09-04 10:00:11

假设您已经有一个实现所有标准登录内容的网站,那么允许用户在特定时间段(例如 30 天)内自动登录的正确和最安全的方法是什么?应严格执行此期限;即,我不认为有效的解决方案是给 cookie 一个过期日期并希望用户的浏览器及时删除它。

现有登录系统的详细信息:

  • 用户必须输入用户名和密码
  • 数据库存储用户名以及 strong_hash_function(password + user_specific_random_salt)
  • 登录表单通过 SSL 加载和提交,所有后续页面也是如此

那里有很多例子,而且很多都有明显的安全漏洞。让我们使用 PHP 作为目标语言,但这些概念应该适用于任何语言。

4个回答

在某些情况下,我的答案不完整并且存在重大缺陷 - 请参阅@Scott 的答案


我的回答分为两部分:

首先,假设您的威胁模型不担心客户端 cookie 的暴露,您可以在服务器端生成并存储一个随机数,使用用户名和其他信息(例如客户端 ip、计算机名、时间戳、类似内容)对其进行哈希处理,然后在cookie中发送。随机数应与到期日期一起存储在数据库中,并在 cookie 返回时进行检查。
这应该只是一个“记忆”cookie,而不是会话 cookie - 即当您在没有会话的情况下获得该 cookie 时,重新发布一个新的常规会话 cookie。另请注意,与会话 cookie 一样,这应该仅通过 https 传递,并使用secureandhttpOnly属性,当然还有适当的 cookie 范围等。

其次,对于被记住的用户,您应该(可能取决于敏感度)为某些敏感操作调用重新身份验证过程。也就是说,有时他们无论如何都需要再次输入密码,但很少,并且仅用于敏感操作。这是为了防止 CSRF 和共享桌面暴露等攻击。

我已经写了很多关于安全登录和“记住我”复选框的文章。公认的答案没有错,但我认为它在某个方面比它需要的要复杂一些,并且忽略了需要更多复杂性的领域。

在服务器端生成并存储一个 nonce,用用户名和其他信息(例如客户端 ip、计算机名、时间戳、类似的东西)对其进行哈希处理,并将其发送到 cookie 中。随机数应与到期日期一起存储在数据库中,并在 cookie 返回时进行检查。

因此,不向客户端公开任何信息的实现可能看起来像......

// Storage:
setcookie(
    'rememberme',
    hash_hmac('sha256', $username . $_SERVER['REMOTE_ADDR'], $storedNonce),
    time() + 8640000 // 100 days, for example
);
// Validation:
$valid = hash_equals(
    $_COOKIE['rememberme'],
    hash_hmac('sha256', $username . $_SERVER['REMOTE_ADDR'], $storedNonce)
);

这里明显的限制是,如果您的 IP 地址(或其他信息)发生变化,您的 cookie 将毫无用处。有些人可能认为这是一件好事,我认为它对 Tor 用户的可用性产生了不必要的敌意。

您当然可以将上述引用解释为不同的意思,例如穷人的 JSON Web 令牌。但是,每个域的 HTTP cookie 限制为 4 KiB。空间非常宝贵。

另一个问题:你的数据库查询泄露了多少关于你的应用程序的信息?(是的,我说的是旁道。)


Paragon Initiative 的安全“记住我”策略

贮存:

  1. random_bytes()从(PHP 7.0+ 或通过random_compat )生成一个随机的 9 字节字符串,base64 将其编码为 12。这将用于数据库查找。
  2. 生成另一个随机字符串,最好至少 18 个字节长,并再次对其进行 base64 编码(到 24+)。这实际上将用于身份验证。
  3. 存储$lookuphash('sha256, $validator)入库;当然与特定的用户帐户相关联。
  4. 存储$lookup . $validator在用户的 HTTP cookie 中(例如rememberme)。

验证(自动登录):

  1. 将 cookie 拆分为$lookup$validator
  2. 基于$lookup;执行数据库查找 如果这里有定时侧信道也没关系。
  3. 检查hash_equals($row['hashedValdator'], hash('sha256', $validator))
  4. 如果第 3 步返回TRUE,请将当前会话与适当的用户帐户相关联。

安全分析:

  • 缓解了哪些侧信道?
    最重要的是:它减轻了时序信息泄漏对数据库查找中使用的字符串比较操作的影响。

    如果您实现了一个简单的随机令牌身份验证,攻击者可能会发送大量请求,直到他们为用户找到有效的身份验证令牌。(他们可能无法选择要冒充的受害者。)

  • 如果攻击者可以泄露长期身份验证数据库表怎么办?
    我们在数据库中存储了 SHA256 哈希$validator,但哈希的明文存储在 cookie 中。由于输入是一个高熵值,因此蛮力搜索不太可能产生任何结果。(这也减轻了对例如 bcrypt 的需求。)

此策略在Gatekeeper中实现。

这是一个有趣的问题。我首先要说的是,我不确定是否可以在不削弱应用程序安全性的情况下做到这一点,但随后取决于可能被认为值得权衡的站点。

所以一种方法可能是

会话状态数据库将需要记录设置令牌的时间和应记住的持续时间,以便它可以比较提供的令牌。

当用户登录到应用程序时,他们有一个复选框,允许他们保持登录状态(理想情况下有一系列时间段供用户选择)

设置会话令牌后,该时间段将用作 cookie 到期时间。在随后的访问中,cookie 将被呈现给应用程序,服务器将需要检查它是否仍然有效(即没有命中记住我功能的到期期限)。如果令牌仍然有效,则将用户视为已通过身份验证,否则将清除令牌并将用户重定向到登录屏幕。

这种方法的问题。共享浏览器意味着未经授权的访问是可能的。

此外,任何可以访问 cookie 的人(通过站点中的漏洞、浏览器问题或通过机器上植入的恶意软件)都可以在未经授权的情况下访问该站点。减轻这种情况的一个常见建议是尝试将 cookie 绑定到源 IP 地址(当然是加密或存储在服务器上),当用户提供 cookie 时会检查该地址,但是这会导致用户可能来的大型公司环境出现问题来自多个 IP 地址,也可能容易受到欺骗。

您不必依赖浏览器来删除 cookie,您可以让网站检查 cookie 上的到期日期。

事实上,为了安全起见,我想说这可能是您无论如何都必须要做的事情,因为您希望将过期日期设置为 cookie 值的一部分并对其进行加密,而不是依赖cookie 本身的纯文本过期属性。

而且您真的想将 cookie 标记为安全,并在单个会话期间使用辅助 cookie,或者使用 SSL 保护整个会话(不仅仅是登录过程)以防止 cookie 被盗并被未经授权的一方使用。