我不明白仅使用 cookie 进行身份验证有什么问题?

信息安全 加密 验证 Web应用程序 饼干 api
2021-09-02 22:45:54

我正在编写一个应用服务器,并且可以选择仅使用安全 cookie 进行身份验证。以下是它的工作方式:

  1. 您在服务器上定义了一个 32 字节的密钥
  2. 当用户登录时,您检查数据库以查看 bcrypt 哈希是否匹配,如果匹配,则调用 request.remember(user_id)
  3. 在需要身份验证(和 user_id)的路由处理程序上,您通过解密 cookie 来解开 user_id,如果它有效,则继续。否则,返回Unauthorized错误。
  4. 如果用户点击我刚刚调用的注销处理程序request.forget(),并且客户端上的 cookie 将被删除。

这一切似乎都奏效了。所以我很好奇为什么不是每个人都这样做?我环顾四周,似乎有很多关于 JWT 的讨论,生成身份验证令牌 UUID 并将它们存储在 Redis/数据库等中。所以显然这种方法不安全?你需要在服务器上存储状态吗?

如果我猜的话,我会说这种方法的一个问题可能是,如果 cookie 以某种方式被盗(不知道如何,因为它是通过 TLS 并且 cookie 是 HTTP-Only 并且如果重要的话是安全的),那么用户将能够被攻击者冒充。但我认为这也适用于其他方案?

我可以看到的另一个问题是,用户可以随机生成身份验证令牌,直到他们找到与用户匹配的身份验证令牌?但我不确定这是否是一个问题,因为我只是对身份验证处理程序进行速率限制,我想这种事情需要一段时间?哦,也许他们可以创建 100 个帐户,看看加密的 cookie 身份验证令牌是什么样子,然后在客户端暴力破解它以找出服务器上的密钥是什么?然后他们就能够通过生成身份验证令牌来冒充用户?虽然我认为找到密钥需要很长时间,因为它是 32 字节?

我猜这种方法不会自动使 cookie 过期(这就是为什么不经常使用它的原因?但我认为有一种方法可以向 cookie 添加过期标头?这行得通吗?)我什至不确定我是否需要过期对于这个应用程序.. 似乎它会惹恼用户。Google/Facebook 等地方的会话持续多长时间?我觉得我总是永远登录这些服务。

我不知道。我觉得我在这里错过了很多信息。有没有什么地方可以找到所有这些不同方法的优缺点列表?

3个回答

您描述的方案可能容易受到跨站请求伪造(CSRF)的影响。

可以代表用户发出恶意请求。该请求将发送通过身份验证检查的 cookie,并执行用户自己可以执行的任何活动。

https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)

如果您查看上面关于 CSRF 的 OWASP 文章的链接,它特别将秘密 cookie 列为无效缓解技术。

假设您使用 cookie 作为无状态身份验证标识符,那么您的方案基本上类似于 JWT,除了使用 cookie 创建。主要区别在于:

  1. JWT 通常只签名而不是加密(尽管您也可以加密它们)。这意味着您可以要求第三方服务为您创建代币。这允许您将应用程序插入其他身份验证服务或自己为其他应用程序提供身份验证服务。
  2. 移动原生应用程序和 cookie 可能会产生问题。通常可以克服,但实现可能比使用令牌要复杂得多,令牌只需要作为文本附加到请求标头或正文并易于存储。
  3. JWT 更易于使用,尤其是在 javascript 环境中,因为您几乎可以将任何您想要的元数据附加到声明中,并且可以像读取普通 JSON 一样读取它们。它还有助于随着它们的流行,您可以为更多现代框架找到现成的解决方案。
  4. Cookie 容易受到跨站点请求伪造 (CSRF) 的影响,而令牌将取决于您如何存储它们(如果在 Web 存储中,则为跨站点脚本 (XSS),如果您决定存储 cookie,则为 CSRF)。

总之,您的建议是可靠的,但它与 JWT 基本相同,缺少一些好处。如果您的系统不需要第三方集成,那么一定要使用 cookie,但要确保它们免受 @Daisetsu 提到的 CSRF 的影响。

你基本上是对的。cookie 是加密的,所以内容不能被看到或弄乱。(除非加密被破坏。)cookie 仅通过 TLS 发送,因此它不能被 MITM 窃取。(除非您的证书以某种方式受到损害。) cookie 设置为仅 HTTP,因此任何恶意 JS 都无法窃取它。从所有有意义的角度来看,它都是安全的。

我对你的设置只有一个抱怨。您正在为该 cookie 中的用户使用持久标识符。万一出现问题,妥协将持续您的用户记录的整个生命周期。生成一些随机字符串,将其存储在某个地方,并使用它来引用您的用户。这样,您就可以不断地循环使用 ID 用户的内容,其中包含对会话生命周期的任何破坏。

还可以考虑开放流行的框架并研究它们如何处理这个问题。您可能会选择一些有用的技术来使事情变得更安全或更高效。

至于 JWT 等,它们既是人们想要使用的新亮点,又是一种真正有用的技术,可以使身份验证存在于一个单独的系统上,而不是用户需要进行身份验证的地方。

编辑:对 Laravel 进行了一些研究,看看他们是如何做到的。他们只是将用户 ID 存储到会话中。会话是服务器端的,因此是安全的。这将它与会话处理区分开来,这似乎是正确的。会话标识符是一个随机的 40 个字符的字符串。这被设置为 HTTP-Only。