"Refresh Token" 的目的是什么?

What is the purpose of a "Refresh Token"?

我有一个与 YouTube 直播集成的程序 API。它在计时器上运行,因此我可以相对容易地编写程序,每 50 分钟使用刷新令牌获取一个新的访问令牌。我的问题是,为什么?

当我通过 YouTube 进行身份验证时,它给了我一个刷新令牌。然后我使用这个刷新令牌大约每小时获取一次新的访问令牌。如果我有刷新令牌,我总是可以使用它来获取新的访问令牌,因为它永远不会过期。所以我看不出这比从一开始就给我一个访问令牌而不用打扰整个刷新令牌系统更安全。

基本上,刷新令牌用于获取新的访问令牌。

为了清楚地区分这两个标记并避免混淆,下面是它们在 The OAuth 2.0 Authorization Framework 中给出的功能:

  • Access tokens are issued to third-party clients by an authorization server with the approval of the resource owner. The client uses the access token to access the protected resources hosted by the resource server.
  • Refresh Tokens are credentials used to obtain access tokens. Refresh tokens are issued to the client by the authorization server and are used to obtain a new access token when the current access token becomes invalid or expires, or to obtain additional access tokens with identical or narrower scope.

现在,要回答关于为什么仍然向您颁发刷新令牌而不只是保护访问令牌的问题,互联网工程任务组在 Refresh tokens 中提供的主要原因是:

There is a security reason, the refresh_token is only ever exchanged with authorization server whereas the access_token is exchanged with resource servers. This mitigates the risk of a long-lived access_token leaking in the "an access token good for an hour, with a refresh token good for a year or good-till-revoked" vs "an access token good-till-revoked without a refresh token."

如需更详细完整的 OAuth 2.0 Flow 信息,请尝试阅读以下参考资料:

@Teyam 提到 SO post 为什么 OAuth v2 同时具有访问令牌和刷新令牌?但我更喜欢那里的另一个答案:

TL;DR refresh_token 不会带来更高的安全性。它的目的是提高可伸缩性和性能。然后,access_token 可能只存储在一些快速的临时存储(如内存)中。它也允许授权和资源服务器分离。

"So I don't see how this is any more secure than just giving me an Access Token from the start and not bothering with the whole Refresh Token system." 我在同一个问题上挣扎。简短的回答是刷新令牌是确保凭据没有过期所必需的。

一个例子可能会有所帮助: 我有一个存储您的病历的数据库。您同意与您的配偶分享您的医疗记录。你的配偶使用他们的访问令牌从我的数据库中读取你的记录。两周后,您的配偶再次检查您的医疗记录,刷新令牌用于确保他们仍然有权(来自身份验证服务器)查看您的记录。刷新令牌绕过了您的配偶向身份验证服务器重新输入其凭据(用户名和密码)的需要,但它确实确保他们仍然具有访问资源的合法性。永不过期的访问令牌不会知道您是否撤销了配偶访问您的医疗记录的权利。

刷新令牌至少有两个用途。首先,刷新令牌是一种 'proof' OAuth2 客户端已经 已经 收到用户访问其数据的许可,因此无需再次请求新的访问令牌要求用户完成整个 OAuth2 流程。其次,与长期访问令牌相比,它有助于提高整个安全流程。我将更详细地讨论这两点。

刷新令牌作为一种不打扰用户的方式

我们举个例子来说说第一个目的。假设您作为用户正在使用想要与您的 YouTube 帐户数据进行交互的第三方客户端 Web 应用程序。一旦您授予客户端应用程序使用您的 YouTube 数据的权限,您是否希望客户端应用程序在其 YouTube 令牌过期时再次提示您许可?如果 YouTube 令牌到期时间非常短,比如 5 分钟,会发生什么情况。如果客户端应用程序至少每 5 分钟提示您一次许可,那会有点烦人! OAuth2 对此 'problem' 提出的解决方案是刷新令牌。通过使用刷新令牌,访问令牌可以保持短暂(如果访问令牌以某种方式泄漏或被盗,这是可取的),并且刷新令牌可以保持(呃)长期存在,从而允许客户端获得新的访问权限令牌在不需要用户许可(再次)的情况下过期。

但是为什么要刷新令牌?如果重点是不要用权限请求来干扰用户,那么为什么客户端不能简单地说“嘿,授权服务器,我想要另一个访问令牌。现在!”?或者,“嘿授权服务器,这是我过期的令牌,给我一个新的!”。好吧,刷新令牌用作一种“证明”,即客户端在某个原始时间点被用户授予访问权限。此“证明”采用由授权服务器进行数字签名的刷新令牌的形式。通过客户端出示刷新令牌,授权服务器可以验证客户端是否在过去的某个时间收到了用户的许可,并且客户端不必再次提示用户。

刷新令牌以提高安全性

然而,这提出了一个问题,“嗯,如果刷新令牌泄露或被盗,或者只是被恶意客户端应用程序保留,并且没有根据用户的请求删除它,会发生什么?不能攻击者只是继续使用刷新令牌无限期地获得有效的访问令牌(或直到它过期)?这个问题导致讨论我提到的第二个目的,刷新令牌有助于更安全的流程。

访问令牌出现的问题是,一旦获得,它们只会呈现给资源服务器(例如 YouTube)。因此,如果访问令牌被盗或泄露,您如何告诉资源服务器不要信任该令牌?好吧,你真的不能。唯一的方法是更改​​授权服务器上的私有签名密钥(首先签署令牌的密钥)。我认为这样做很不方便,并且在某些情况下(例如 Auth0)不受支持。

另一方面,刷新令牌需要经常提交给授权服务器,因此如果一个被破坏,那么撤销或拒绝整个刷新令牌是微不足道的,而不必更改任何签名密钥。

access_token使用频率更高,撤销能力不是很重要,因为它们是短暂的。

refresh_token 的使用频率较低,撤销能力至关重要,因为它们可用于生成新的 access_token

验证签名令牌的成本较低,但撤销起来很困难。

验证存储在数据库中的令牌成本高昂,但可以轻松撤销。

因此,签名密钥可以用作 access_tokens 以提高性能。

Db 存储的密钥可以用作 refresh_tokens,以便于撤销它们。

如果没有refresh_token,很难找到一种提供低成本验证和容易撤销能力的机制。因此 refresh_token 由于性能原因而存在。

使用短期访问令牌和长期更新令牌至少有 3 个相关的原因。

不记名代币

来自原问题:

If I have the Refresh Token, I can ALWAYS use this to get a new Access Token, since it never expires.

尽管可能总是能够使用刷新令牌获取新的访问令牌,但攻击者通常可能做不到。这是因为您对刷新令牌的使用与您作为客户的身份证明相结合,例如通过提供您的 client_secret。访问令牌不需要此类证明,访问令牌是不记名令牌,即只需出示它们就足够了。

将访问令牌设置为短暂的会在某种程度上减轻访问令牌的无限能力。

攻击面

访问令牌与(可能很多)资源服务器交换,这增加了泄漏的机会。刷新令牌只与授权服务器交换。

同样,访问令牌的短暂性至少是某种程度的缓解。

撤销

将访问令牌实现为已签名的 JWT 是可能的(并且很常见)。在这种情况下,任何服务器(知道签名方的 public 密钥,通常位于某个众所周知的位置)都可以独立验证访问令牌的正确性。这允许很好地分离架构,在某种意义上,资源服务器不必向授权服务器询问授权。

这种设置的缺点是无法撤销此类令牌(没有像撤销授权服务器的 public 密钥那样激烈的事情)。

通过缩短访问令牌的有效期,可以简单地允许它们 运行 退出而不是显式撤销。

这是来自 OAuth 2.0 documentation 的信息。

Refresh tokens are used to obtain a new access token 当前访问令牌失效或过期时,或获取具有相同或更窄范围的额外访问令牌(访问令牌的生命周期可能比资源所有者授权的更短,权限更少)。

  +--------+                                           +---------------+
  |        |--(A)------- Authorization Grant --------->|               |
  |        |                                           |               |
  |        |<-(B)----------- Access Token -------------|               |
  |        |               & Refresh Token             |               |
  |        |                                           |               |
  |        |                            +----------+   |               |
  |        |--(C)---- Access Token ---->|          |   |               |
  |        |                            |          |   |               |
  |        |<-(D)- Protected Resource --| Resource |   | Authorization |
  | Client |                            |  Server  |   |     Server    |
  |        |--(E)---- Access Token ---->|          |   |               |
  |        |                            |          |   |               |
  |        |<-(F)- Invalid Token Error -|          |   |               |
  |        |                            +----------+   |               |
  |        |                                           |               |
  |        |--(G)----------- Refresh Token ----------->|               |
  |        |                                           |               |
  |        |<-(H)----------- Access Token -------------|               |
  +--------+           & Optional Refresh Token        +---------------+

(A) 客户端通过身份验证请求访问令牌 授权服务器并提供授权许可。

(B) 授权服务器对客户端进行认证并验证 授权授予,如果有效,则颁发访问令牌 和一个刷新令牌。

(C)客户端向资源发起受保护的资源请求 服务器通过出示访问令牌。

(D) 资源服务器验证访问令牌,如果有效, 满足要求。

(E) 重复步骤 (C) 和 (D),直到访问令牌过期。如果 客户端知道访问令牌过期,它跳到步骤(G); 否则,它会发出另一个受保护的资源请求。

(F) 由于访问令牌无效,资源服务器returns 无效令牌错误。

(G) 客户端通过身份验证请求新的访问令牌 授权服务器并提供刷新令牌。这 客户端身份验证要求基于客户端类型 以及授权服务器策略。

(H) 授权服务器对客户端进行认证并验证 刷新令牌,如果有效,则发出一个新的访问令牌(并且, 可选,一个新的刷新令牌)。

访问令牌的生命周期很短。一旦它过期,您需要一个新的访问令牌来访问受保护的资源。获取新访问令牌的一种方法是再次验证资源所有者并获得授权,然后获取访问令牌。但是,这会很烦人。

这个问题可以用刷新令牌解决。它的使用寿命很长。因此,您可以使用它来获取新的访问令牌,而无需与资源所有者进行交互。

好吧,你可能会想用长 life-span 的令牌来获得另一个短 life-span 的密钥有什么意义。好吧,即使刷新令牌被泄露,攻击者也不能仅仅从中获取访问令牌。原因是攻击者需要客户端凭据以及刷新令牌。

因此,access token会有一个简短的life-span(原因见其他答案),以提高安全性。为了避免资源所有者在访问令牌过期时感到烦恼,OAuth 使用刷新令牌。

仅使用一种替代方案 Access Token 比使用两种替代方案 Access TokenRefresh Token[= 风险更大52=].

例如,如果您只使用一种替代方案访问令牌,则将100 天长有效期设置为 Access Token 最后 Access Token 被黑客窃取,黑客获得了 自由使用 Access Token 的大好机会100 天 作为不良目的的最大值。

但是,如果您使用 Access TokenRefresh Token 这两个选项,则将 较短的到期日期设置为 60 分钟 to Access Token and 100 days to Refresh Token 最后访问令牌 被黑客窃取,黑客获得 自由使用访问令牌 60 分钟的机会要小得多 作为不良目的的最大值。

现在,你会想Refresh Token是不是被盗了。实际上,如果 Refresh Token 被黑客窃取,黑客仍然有 大机会免费使用 的 Refresh Token 100 天 作为不良目的的最大值。但是 Refresh Token 被盗的概率远小于 Access Token 被盗的概率,因为 Refresh Token 是使用每 60 分钟仅一次刷新访问令牌(获取一个新的访问令牌)而访问令牌被使用每次你访问资源这是更频繁的。

因此,您最好同时使用 Access TokenRefresh Token

这是一次很好的学习体验,让我对令牌、刷新令牌和缓存有了一些了解。但是,(我很好奇,我在这里不提供任何建议)我们可以在使用 Microsoft Identity 平台时使用用户登录后 returns 的代码吗?我们可以只存储 CodeIdToken,并在需要时使用它来获取新的访问令牌吗?因为我认为我们使用它来获取访问令牌,那么我们是否应该每次都使用它来重新生成访问令牌?

 ...
 ResponseType = OpenIdConnectResponseType.CodeIdToken,
...

private async Task OnAuthorizationCodeReceived(AuthorizationCodeReceivedNotification context)
    {
        IConfidentialClientApplication clientApp = MsalAppBuilder.BuildConfidentialClientApplication();
        AuthenticationResult result = await clientApp.AcquireTokenByAuthorizationCode(new[] { "User.Read" }, context.Code)
          .ExecuteAsync();
    }
    

refresh_token 模式使 OAuth 服务器处于控制之中,因此服务器可以在 坏事 发生时进行干预,例如 access_tokenrefresh_token 被泄露.

例如

如果access_tokenrefresh_token落入黑客手中,access_token将很快过期,黑客可能会尝试刷新令牌,但服务器现在有capacity/control 不再发出 access_token(考虑到服务器已获取泄漏信息)。