我们应该在我们的数据库中为 oauth2 存储 accesstoken 吗?

信息安全 验证 oauth 授权 Facebook
2021-08-08 02:37:03

我需要在我的 Web 应用程序中实现 Facebook 和 Google 登录。我还需要访问用户的 Facebook/Google+ 好友列表。我已经浏览了 Facebook 和 Google 的完整 OAuth2 文档。我理解了基本概念。例如,假设 Facebook 登录的步骤是:

  1. 用户将点击“FB登录”按钮。
  2. 用户将被要求登录 Facebook 并获得许可。如果用户允许,它将返回一个授权码。
  3. 现在我们将使用授权码来获取访问令牌。
  4. 我们可以将访问令牌存储在会话中以启动用户会话。
  5. 现在我们可以使用访问令牌来访问不同的用户资源。

现在我在第 3 步之后有些困惑。我们应该在每次用户登录时生成访问令牌还是将访问令牌存储在我们的数据库中?

如果我们将访问令牌存储在我们的数据库中,当用户在 10 天后访问我们的网站(假设他清除了浏览器 cookie)并再次单击“FB 登录”按钮时,我们如何重用它。因为当用户再次单击“FB 登录”按钮时,他将获得一个新的授权码,并且必须重新开始整个过程​​。如何识别该用户在我的数据库中已经拥有访问令牌?

任何帮助将不胜感激。

4个回答

从技术上讲,您可以将访问令牌存储在数据库中,并将其用于 API 调用,直到过期。不过,这可能比它的价值更麻烦。

一方面,正如 Jonathan 在上面的评论中指出的那样,现在您必须担心保护您的数据库和其中的数据 - 这些令牌可以访问有关您的用户的一些相当特权的信息。当然,简单地将令牌存储在会话存储中也可能会将其放在磁盘上,具体取决于您的会话配置。在您不使用它时对其进行加密是一个好主意。

您提出的关于用户清除 cookie 并返回的方案也是一个问题。您可以从数据库中获取访问令牌并将其粘贴回他们的 cookie 中,但在您这样做之前,您必须确保他们是他们所说的人 - 现在您必须再做一层密码才能给他们访问他们已经给你的令牌。

当他们回来并再次单击登录按钮时,您最好简单地重新执行授权流程。它没那么贵。但是,如果这对您来说确实是一个亮点,那么存储令牌是一种选择。您只需要非常小心地处理所有相关问题。

我一直在考虑这个问题,可能想出了一个对我们有用的答案,但我不能说它是否对你有用。

在我们的环境中,我们可能需要使用访问令牌的主要原因是在某些自动化或后端过程完成后或按计划代表用户进行操作。在这种情况下,我们不能简单地要求用户再次登录,因为用户没有直接参与此工作流程(他会要求完成工作,但完成时不在那里)。所以我们必须以某种方式访问​​访问令牌。

当然,如果不会引起问题,我也想在可行的情况下跳过重新授权。

但我也不喜欢在 Web 服务器上存储使用访问令牌所需的所有内容的想法。因此,即使我在数据库中加密了访问令牌,如果加密密钥存储在 Web 服务器上,它并不能真正缓解我的恐惧。哎呀,客户端密码和应用程​​序 ID 也在那里,这就是一切。

所以这是我提出的解决方案,它需要四个参与者:

Web 服务器存储 appid 、secret 和数据库连接字符串(当然)。当它获得一个应用令牌时,它会生成一个随机对称加密密钥并加密访问令牌。数据库获取加密的访问令牌,并且加密密钥存储在客户端 cookie中。同时,Web 服务器将加密密钥和用户 ID作为一对发送到后端系统。

客户端在使用站点时,通过客户端cookie解密数据库中的access token,可以避免重新认证;如果 cookie 消失,无论如何都必须重新进行身份验证。后端系统(它的攻击面比 Web 服务器小得多)也有一个 DB 连接字符串,因此它是唯一一个存放所有必要信息以与用户信息交互的地方;它可以在访问令牌的整个生命周期内随意使用这些信息。

这使得 Web 服务器只能临时访问任何访问令牌,并且令牌永远不会存储在客户端上。这对我来说似乎很安全,尽管也许有人会说它设计过度。想法?

我认为访问令牌及其使用方式存在一些混淆,这可能会导致安全问题。

令牌可能存在安全风险是正确的,但这取决于您从服务中要求的信息。例如,朋友列表比名字和姓氏提供更多信息,但这些信息不如个人信息重要。无论哪种情况,您都不想公开实际的访问令牌,因为这就像向您的应用程序公开密码一样。

如果您愿意,我选择制作一个辅助“令牌”,它保存一个会话值(例如 cookie 中的加密会话值),用于识别用户直到它过期。所以我在数据库中有访问令牌(应该是加密的,为了安全起见),可以访问用户信息。

您还可以通过令牌检索此人的 ID。如果您至少将其存储在数据库中,则可以通过此人的 ID 匹配检索到的令牌。这样,当您交换访问令牌的代码时,您可以比较 ID 并找到正确的记录。

这些答案中有很多都是过时的,因为它们是在 JSON Web Tokens (JWT) 广泛采用之前编写的。

您应该将 access_token 视为手头有电子邮件/密码对,因此需要安全地存储和传输它。一个 access_token 不应该包含太多信息,sub一个 JWT 范围内的用户 ID 就足够了。由于 JWT 可以防止恶意修改有效负载,因此您可以安全地使用存储在那里的授权和 ID。

对于大多数应用程序来说,在服务器上存储 access_token 可能有点麻烦,因为您可以在 access_tokens 上使用短暂的到期时间,而是存储一个 refresh_token(不太频繁的数据库调用)。如果您需要用户信息,请将其存储在 ID 令牌中,并仅用于显示有关经过身份验证的用户的信息。

每当您想刷新该信息时重新请求 ID 令牌,并且永远不要向 API 提交 id_token。

如今,聪明的开发人员实施 OpenID Connect 以在 OAuth 2.0 之上标准化他们的用户信息和访问权限,我将从那里开始,因为大多数身份提供商 (IdP) 都实施了这个规范(即 Google、Facebook、Auth0、YourServiceHere)。