为什么我应该将 OAuth 2 访问令牌与刷新令牌一起保留

信息安全 验证 密钥管理 oauth
2021-09-09 05:15:11

这是我在几篇关于 OAuth 2 的文章中遇到的:在将刷新令牌持久保存到数据库时,一些作者也更喜欢存储访问令牌,或者至少提到它是你应该做的事情。当涉及到基于refresh_token从数据库中获取的票证授予访问权限时,将对其进行反序列化、更新并使用新的刷新令牌发送给用户。

这是一个RefreshTokens表的示例,其中ProtectedTicket列保持序列化access_tokens发给用户:

在此处输入图像描述

到目前为止,我可以想到至少 3 个反对这种方法的原因:

  1. 当您保留用户的访问票证,只是等待有人抓住它们并重用于不可疑的资源服务器时,这不是一个安全威胁吗?不能使用单向哈希,因为我们需要反序列化、更新并将其发送回用户,而且我们也不想保留有效的访问令牌。

  2. 例如,您想不时更新用于序列化/反序列化票证的加密密钥,以防万一。如果您不希望您的用户在过期后被注销access_token,您必须关心更新那些持久的票证。

  3. 您必须根据您对#1#2的关注程度来编写大量代码。甚至可能您在 OAuth 框架周围实施了一些肮脏的技巧,因为它可能无法提供您需要的扩展点。

所以问题是,你为什么要做这一切?应该给予什么样的优势才能真正让它值得所有的麻烦?验证后制作一张全新的票不是更容易更安全、更易于维护refresh_token吗?

2个回答

散列 accessToken 的意义何在?

我们散列密码是因为人们到处重复使用它们。如果他们不重用密码,我们就不需要对密码进行哈希处理,毕竟应用程序已被黑客入侵(因为攻击者获得了对数据库的访问权限),所以一切都已经丢失了。

accessTokens 不被重用并增加安全性,仅使用一次(例如 10 分钟,具体取决于您的令牌生命周期)。那么为什么我们需要对它们进行哈希处理呢?RefreshTokens 可用于获取多个 accesstokens,这就是为什么对它们进行哈希处理似乎是合理的,但即使这样也是有争议的,因为 refreshTokens 不会跨应用程序使用。

如果您真的想要,您可能会考虑加密 accessTokens,但我们假设您的整个应用程序已被黑客入侵,包括加密密钥。顺便说一句,您并没有真正存储 accessToken,而是存储票证以获取 accessToken,但这不会改变您问题的相关性。

答案很可能与性能有关。

OAuth2 访问令牌被设计为短暂的,而刷新令牌被设计为更长寿,因为它们可用于获取新的访问令牌。大多数客户端库旨在缓存 OAuth 访问令牌以避免访问授权服务器。看这里:

https://github.com/google/google-api-dotnet-client/blob/master/Src/Support/GoogleApis.Auth/OAuth2/UserCredential.cs

/// <summary>
/// Default implementation is to try to refresh the access token if there is no access token or if we are 1 
/// minute away from expiration. If token server is unavailable, it will try to use the access token even if 
/// has expired. If successful, it will call <see cref="IAccessMethod.Intercept"/>.
/// </summary>
        public async Task InterceptAsync(HttpRequestMessage request, CancellationToken taskCancellationToken)
[...]

如果您访问 Google OAuth2 游乐场,您可以看到其中的一些实际操作: https ://developers.google.com/oauthplayground/

当您授权 API 时(只需尝试在自定义范围中键入“配置文件”并单击“授权 API”,如果您不想单击列表),在进行身份验证交换并接收访问/刷新令牌之后,您将看到以下内容:

“访问令牌将在 [countdown] 秒后过期。

[ ] 在令牌过期之前自动刷新令牌。”

通过缓存或存储访问令牌,您可以避免往返授权服务器以将刷新令牌交换为访问令牌(防止延迟和授权服务器中断)。某些服务器还可能会限制您将刷新令牌交换为访问令牌的频率(例如,生成访问令牌可能涉及它们的“昂贵”加密操作)。

泄漏访问令牌是“不好的”,因为它可以在没有任何其他信息的情况下使用,以断言授予它的人的身份 - 但由于访问令牌的寿命有限,增加了压力攻击者立即提示/使用这些值。泄漏刷新令牌(理论上)“更糟”,因为它允许永久访问,但是使这更复杂的是,进行刷新 <-> 访问令牌交换还需要了解您的 OAuth2 client_secret。

在您描述的当前模型中,尚不清楚 ProtectedTicket 是否已加密(您提到它是序列化的,但稍后会谈到更新持久票证)。如果您正在加密数据库中的访问令牌以保护它们不被具有管理数据库访问权限的人(误用),您可以查看处理版本化密钥的解决方案,这将允许您轮换加密密钥。我熟悉的一个是https://github.com/google/keyczar,有人为此创建了 .NET 绑定(我从您的屏幕截图中假设您在 .NET 上)。

KeyCzar 可能会提供一种简单的方法来解决您的一些问题,但是您系统的许多实际安全属性将取决于您的威胁模型和数据库架构之外的许多因素。