为什么OAuth v2同时有访问令牌和刷新令牌?

830

OAuth 2.0协议草案的第4.2节指出,授权服务器可以返回一个access_token(用于认证资源),以及一个refresh_token,后者仅用于创建新的access_token

https://www.rfc-editor.org/rfc/rfc6749#section-4.2

为什么需要两个令牌?为什么不让access_token的有效期与refresh_token相同,而不提供refresh_token呢?

21个回答

668
由Catchdave提供的讨论链接中,还有Dick Hardt提出的另一个有效观点 (原始死链接),我认为值得在此提到比上面写的更多:
“我的刷新令牌的回忆是关于安全和吊销。 & lt;... & gt; < strong>吊销:如果访问令牌是自包含的,则可以通过不发行新的访问令牌来吊销授权。资源不需要查询授权服务器以查看访问令牌是否有效。这简化了访问令牌的验证,并使其更易于扩展和支持多个授权服务器。访问令牌有效但授权被吊销时有一个时间窗口。”
实际上,在资源服务器和授权服务器是同一实体的情况下,并且用户与任一方之间的连接(通常)同样安全时,没有必要将刷新令牌与访问令牌分开。

正如引文中提到的,刷新令牌的另一个作用是确保用户可以随时撤销访问令牌(例如通过其个人资料中的 Web 界面),同时保持系统的可扩展性。

通常,令牌可以是指向服务器数据库中特定记录的随机标识符,也可以包含所有信息(当然,这些信息必须进行签名,例如使用 MAC)。

长期访问令牌的系统应如何工作

服务器通过发放令牌允许客户端在预定义的一组范围内访问用户数据。由于我们希望保持令牌可撤销,因此我们必须将令牌与设置或取消“已撤销”标志的数据库一起存储(否则,如何处理自包含的令牌?)。数据库可以包含多达 len(users) x len(registered clients) x len(scopes combination) 条记录。然后,每个 API 请求都必须命中数据库。尽管对这样的数据库执行 O(1) 查询相当简单,但单点故障本身可能会对系统的可扩展性和性能产生负面影响。

长期有效的刷新令牌和短期有效的访问令牌系统应该如何工作
在这里,我们发放两个密钥:随机的刷新令牌及其对应的数据库记录,以及签名的自包含访问令牌,其中包括过期时间戳字段等。
由于访问令牌是自包含的,我们根本不必访问数据库来检查其有效性。我们只需要解码令牌并验证签名和时间戳即可。
尽管如此,我们仍然需要保留刷新令牌的数据库,但对该数据库的请求数量通常由访问令牌的生命周期定义(生命周期越长,访问率越低)。
为了撤销客户端对特定用户的访问权限,我们应将相应的刷新令牌标记为“已撤销”(或完全删除),并停止发放新的访问令牌。显然,在刷新令牌被撤销但其访问令牌仍然有效的窗口期间,存在一些问题。
权衡
刷新令牌部分消除了访问令牌数据库的单点故障(SPoF),但它们也有一些明显的缺点。
“窗口”。在“用户撤销访问权限”和“访问权限被保证已被撤销”的事件之间的时间段。
客户端逻辑的复杂性。 没有刷新令牌: 1. 使用访问令牌发送 API 请求。 2. 如果访问令牌无效,则失败并要求用户重新进行身份验证。 有刷新令牌: 1. 使用访问令牌发送 API 请求。 2. 如果访问令牌无效,则尝试使用刷新令牌更新它。 3. 如果刷新请求通过,则更新访问令牌并重新发送初始的 API 请求。 4. 如果刷新请求失败,则要求用户重新进行身份验证。
我希望这个答案是有意义的,并能帮助某些人做出更深思熟虑的决定。我还想指出,一些知名的 OAuth2 提供者,包括 GitHub 和 Foursquare 采用了不含刷新令牌的协议,并且似乎对此感到满意。

7
如果我理解正确的话,刷新令牌可以保存在数据库中,并且可以随时删除以撤销访问权限,那么为什么不直接保存访问令牌本身呢? - kosnkov
48
我的翻译如下:如果你在数据库中保存访问令牌(access token),那么每次访问API时都需要查询数据库(这可能对你的情况有好处也可能没有)。如果你保存刷新令牌(refresh token)并保持访问令牌是“自包含”的,那么只有当客户端决定刷新访问令牌时才需要查询数据库。 - Roman Imankulov
19
就我个人而言,如果不打算牺牲安全性(即使仅在某个时间段内),我并不喜欢通过不访问数据库来提高性能的方法。我们应该能够立即撤销访问令牌(access_token)的权限,因为几乎总是涉及敏感用户信息(否则我们可能根本不会使用 OAuth)。我想知道像 Facebook 和 Google 这样的大公司采用了哪种方法。 - Bitcoin Cash - ADA enthusiast
3
“尽管如此,我们仍然需要保留刷新令牌的数据库。” -> 不是吗?我们可以只保留访问令牌的数据库,但只有在收到过期的访问令牌时才进行查询!对吧?或者我漏掉了什么。 - aderchox

547
刷新令牌的想法是,如果访问令牌被攻击者获取,由于其存在时间有限,攻击者只有一个受到滥用的有限时间窗口。
如果刷新令牌被攻击者获取,则无用,因为攻击者需要客户端ID和秘密以及刷新令牌才能获得访问令牌。
话虽如此,由于对授权服务器和资源服务器的每个调用都是通过SSL进行的——包括请求访问/刷新令牌时的原始客户端ID和密钥——我不确定访问令牌比长期有效的刷新令牌和客户端ID /密钥组合更容易“受到威胁”。
当然,这与您不控制授权和资源服务器的实现不同。
以下是关于使用刷新令牌的好帖子: OAuth Archives
引述上述内容,谈论刷新令牌的安全目的:
刷新令牌...减轻了长期访问令牌泄漏的风险(在不安全的资源服务器上记录文件查询参数、测试版或编码不良的资源服务器应用程序、将访问令牌放入cookie中的非https站点上的JS SDK客户端等)。

15
Catchdave说得没错,但我想补充一下自他最初回答以来事情有所发展。现在使用SSL是可选的(当Catchdave回答时可能仍在讨论中)。例如,MAC令牌(目前正在开发中)提供了使用私钥对请求进行签名的功能,因此不需要SSL。因此,刷新令牌变得非常重要,因为您希望具有短寿命的MAC令牌。 - AlexGad
71
如果刷新令牌被攻击者获取,那么攻击者除了刷新令牌还需要客户端id和秘钥才能获取访问令牌,因此刷新令牌失效。但是客户端id和秘钥也存储在设备中,如果攻击者可以访问这些信息,他就能够获取到它们。那么这样做还有意义吗?在 https://github.com/auth0/lock/wiki/Using-a-Refresh-Token 中写道,如果丢失了刷新令牌,攻击者可以请求任意数量的授权令牌,可能不是在 Google 场景下,但如果我正在实现自己的 Oauth2 服务器呢? - Jamsheed Kamarudeen
60
攻击者需要客户端ID、密钥和刷新令牌才能获取访问令牌。那么,使用刷新令牌和重新登录有什么区别呢? - sp00m
48
刷新令牌可以被第三方使用,而无需知道用户凭据即可更新访问令牌。 - Marek Dec
41
客户端 ID 和密钥是 OAuth 客户端的凭据,而不是用户的凭据。在谈论 OAuth 时,“客户端”通常是一个服务器(例如 stackoverflow Web 服务器),它与授权或资源 API 服务器(例如 Facebook 授权提供者)进行接口。用户的凭据仅在用户和 OAuth API 服务器之间传递,从未知晓于客户端。客户端密钥仅从客户端传递给 OAuth API 服务器,并且用户从未知晓。 - machine yearning
显示剩余9条评论

273
尽管以上的回答都很好,但我作为一个安全硕士生和曾在 eBay 工作过的程序员,当我研究买家保护和欺诈时,可以说将访问令牌和刷新令牌分开具有在骚扰用户进行频繁的用户名/密码输入和保持对潜在服务滥用访问权限的控制之间达到最佳平衡的优点。
考虑以下情况。您向用户发放 3600 秒的访问令牌和比一天更长得多的刷新令牌。
1. 用户是一个好用户,在家里用 iPhone 购物和搜索,IP 地址没有变化,对服务器的负载非常低,每分钟只有 3-5 次页面请求。当他的 3600 秒访问令牌过期时,他需要使用刷新令牌获取新的访问令牌。我们在服务器端检查他的活动历史记录和 IP 地址,认为他是一个人类并自觉守法。我们向他授权新的访问令牌以继续使用我们的服务。在刷新令牌本身的一天生命周期结束之前,用户不需要再次输入用户名/密码。
2. 用户是一个粗心的用户。他住在美国纽约,并且他的病毒程序被关闭,被波兰的黑客攻击了。当黑客获得访问令牌和刷新令牌后,他试图冒充用户并使用我们的服务。但是,在短暂的访问令牌过期后,当黑客尝试刷新访问令牌时,我们在服务器上注意到用户行为历史记录中出现了戏剧性的 IP 更改(嘿,这个家伙在美国登录,然后在仅仅 3600 秒之后在波兰刷新访问???)。我们终止刷新过程,使刷新令牌失效,并提示重新输入用户名/密码。
  • 这位用户是一个恶意用户。他想要利用机器人每分钟调用我们的API 1000次来滥用我们的服务。他可能会一直这样做,直到3600秒后他尝试刷新访问令牌时,我们注意到了他的行为并认为他可能不是人类。我们拒绝和中止刷新过程,并要求他重新输入用户名/密码。这可能会潜在地破坏他的机器人的自动流程, 至少会使他感到不舒服。

  • 您可以看到,在我们尝试平衡工作、用户体验和被盗令牌的潜在风险时,刷新令牌表现得非常出色。您在服务器端的看门狗可以通过检查更多的IP更改、API调用频率等来确定用户是否是好用户。

    另外,您也可以通过在每个API调用上实施基本的IP监控或其他措施来尝试限制被盗令牌/滥用服务的损害控制。但这是昂贵的,因为您必须读写有关用户的记录,并会减慢您的服务器响应速度。


    98
    这些是一些优秀的政策和想法,但我在你的回答中没有看到任何必须使用刷新令牌的本质要求。所有这些功能都可以仅使用访问令牌来实现。 - Evert
    17
    使用访问令牌和刷新令牌的好处之一是,访问令牌可以短暂有效,因此不需要与最初颁发它们的服务器进行检查即可无条件地信任它们,这样可以使非关键部分的基础设施能够在没有直接访问用户帐户信息的情况下信任(签名的)令牌中存储的信息,从而实现基础设施的扩展。 - Avi Cherry
    10
    是的,访问令牌可以是短暂的,并且如果用户仍然有效,它也可以被刷新。这不需要使用刷新令牌来完成。 - Rick Jolly
    26
    我认为这个回答假设我们从不希望资源服务器自己进行高级访问控制(例如检查 IP 活动并与各种数据库进行对比等),而是只能依靠在完全隔离的情况下验证访问令牌。虽然在规模上这可能很明显(出于性能方面的考虑),但对于其他帖子和评论中的混淆,对每个人来说显然并不明显。这是一篇很好的帖子,并提供了很好的信息,但我觉得它大大地忽略了原始问题的重点。我建议至少将上述假设明确说明。 - tne
    如果场景仅使用访问令牌实现,则需要将访问令牌延长至1天。在此期间,您将无法对授权服务器进行其他检查。如果您在每个请求上执行这些检查,这意味着您实际上使用了刷新令牌。如果您这样做,将会负载授权服务器(考虑每秒1000次调用,并且在每个请求中询问身份验证服务器是否准确,并将其乘以用户数量)。 - raisercostin
    显示剩余3条评论

    89

    这两个答案都没有涉及刷新令牌存在的核心原因。显然,您可以通过向认证服务器发送客户端凭据来获取新的访问令牌/刷新令牌对 - 这就是您首次获取它们的方式。

    因此,刷新令牌的唯一目的是限制将客户端凭据发送到身份验证服务的使用。访问令牌的TTL(生存时间)越短,客户端凭据需要用于获取新的访问令牌的次数就越多,因此攻击者就有更多机会来破坏客户端凭据(尽管如果使用非对称加密将它们发送出去,这可能会非常困难)。因此,如果您拥有一个单次使用的刷新令牌,则可以使访问令牌的TTL任意小,而不会危及客户端凭据。


    29
    有趣的是,在谷歌的情况下,当您请求刷新令牌时,还需要发送客户端ID和客户端密钥。因此,您无论如何都在每小时泄露信息。 - Rots
    6
    “唯一目的”这个说法站不住脚。将访问令牌的TTL设置为与想象中的刷新令牌相同长,将会达到同样的效果。 - Rhubarb
    12
    由于标准要求客户端凭据与刷新令牌一起发送,因此这个答案的前提是完全错误的。"因为刷新令牌通常是用于请求额外访问令牌的长期凭证...客户端必须通过授权服务器进行身份验证。"另请参见@Rots的评论。 - Kevin Christopher Henry
    18
    A)我认为您混淆了客户端密钥和用户密钥。客户端密钥从不从用户设备发送,只从访问后端应用程序发送到数据提供后端应用程序。B)允许公共客户端(无法保留客户端密钥的客户端,如本机或JavaScript应用程序)进行密码授权的oAuth服务器还将为该公共客户端提供刷新令牌授权,因此在刷新您的令牌时无需发送客户端密钥。C)刷新令牌为后端提供了“心跳”,以检查用户有效性的时间。 - Andreas Lundgren
    3
    此回答是错误的,因为Andreas Lundgren所述。 - Hans Z.
    显示剩余6条评论

    86
    为了澄清一些混淆,您需要理解 客户端密钥 和用户密码的作用,它们之间非常不同。 客户端 是一个由服务器支持的应用程序/网站/程序等,想要通过使用第三方身份验证服务来对用户进行认证。客户端密钥是已知于此客户端和认证服务器的(随机)字符串。使用此密钥,客户端可以向认证服务器标识自己,从而接收请求访问令牌的授权。
    要获取初始访问令牌和刷新令牌,需要以下信息:
    • 用户ID
    • 用户密码
    • 客户端ID
    • 客户端密钥
    然而,要获得刷新的访问令牌,客户端 使用以下信息:
    • 客户端 ID
    • 客户端密钥
    • 刷新令牌
    这清楚地显示了差异:在进行刷新时,客户端使用其客户端密钥接收刷新访问令牌的授权,并因此可以使用刷新令牌而不是用户 ID + 密码重新对用户进行身份验证。这有效地防止了用户不得不重新输入他/她的密码。
    这也表明,失去刷新令牌是没有问题的,因为客户端ID和密钥不知道。它还表明,保持客户端 ID 和客户端密钥的机密性是至关重要的。

    我不理解最后一句话。客户端ID和客户端密码也可能会丢失,但由于TLS的存在并不容易。它们通常是由一些粗心的公共应用程序(如移动应用和网站)暴露给外界而丢失的。考虑到这一点,刷新令牌唯一的作用就是减少用户名:密码链在其他潜在疏忽的服务之间的网络通信中的暴露。 - downvoteit
    一个网站不太可能失去这些数据,因为它们仅在后端处理,而不是在前端。移动应用程序则是另一回事。 - Adversus
    这种情况并不常见,但我需要修复几个这样的Web应用程序前端。可能是因为“密码”授权类型(单步)比“授权码”(两步)更容易实现。 - downvoteit

    58

    这篇答案是由两位资深开发人员(约翰·布雷顿和大卫·詹纳斯)共同完成的。

    使用刷新令牌的主要原因是减少攻击面。

    假设没有刷新密钥,让我们通过以下示例进行说明:

    一栋建筑有80个门。所有的门都用同一把钥匙打开。钥匙每30分钟更换一次。在30分钟结束时,我必须将旧钥匙交给钥匠并获得新钥匙。

    如果我是黑客并得到了你的钥匙,那么在30分钟结束时,我会将其送到钥匠那里并获取一个新钥匙。我将能够持续打开所有门,而不管钥匙是否更改。

    问题: 在30分钟内,我有多少次攻击机会?我有80次攻击机会,每次你使用钥匙(将访问令牌传递以识别自己)时都会增加一次。所以攻击面为80X。

    现在让我们通过相同的示例来看一下,但这次假设有一个刷新密钥。

    一栋建筑有80个门。所有的门都用同一把钥匙打开。钥匙每30分钟更换一次。要获得新钥匙,我不能传递旧的访问令牌。我必须只传递刷新密钥。

    如果我是黑客并且拥有你的密钥,我可以在30分钟内使用它,但在30分钟结束后将其发送给钥匠没有任何价值。如果我这么做了,那么钥匠会说“此令牌已过期。您需要刷新令牌。”要延长我的黑客攻击,我必须黑进快递员到钥匠。快递员有一个独特的密钥(将其视为刷新令牌)。
    问题:在30分钟内,对于刷新密钥我有多少次黑客攻击机会?80次吗?不,我只有1次黑客攻击机会。在快递员与钥匠通信期间就是1个攻击面。我确实有80次黑客攻击机会,但在30分钟后它们都毫无用处。
    服务器将根据凭据和签署的JWT验证访问令牌。
    访问令牌泄漏很糟糕,但一旦它过期,攻击者就再也不能利用它了。刷新令牌泄漏更糟糕,但可能性较小。(我认为有理由质疑刷新令牌泄漏的可能性是否比访问令牌泄漏低得多,但这是想法。)
    重点是访问令牌将添加到您发出的每个请求中,而刷新令牌仅在刷新流程中使用。因此,中间人攻击看到令牌的机会较小。

    频率会帮助攻击者。SSL中类似于Heartbleed潜在的安全漏洞、客户端中潜在的安全漏洞以及服务器中潜在的安全漏洞都可能导致信息泄露。

    此外,如果授权服务器与处理其他客户端请求的应用服务器分开,则该应用服务器永远不会看到刷新令牌。它只会看到很快就会过期的访问令牌。

    隔离有助于保障安全。

    最后,刷新令牌可以被轮换。这意味着“每次客户端请求用于获取新访问令牌的刷新令牌时,都会返回一个新的刷新令牌”。由于刷新令牌不断交换和失效,威胁得到了降低。举个例子:令牌通常在TTL(生存时间)之后失效,通常为一小时。

    刷新令牌并不总是在使用后撤销并发出新的。这意味着,如果您遇到网络故障,在检索新的刷新令牌时,下一次发送该刷新令牌时,它将被视为已撤销,您需要重新登录。

    关于轮换,更多信息请参见 此处此处

    概述

    • 降低刷新频率
    • 隔离
    • 轮换(更快地使token失效)和更加细粒度的管理(过期时间或请求次数)。

    所有这些都有助于减轻威胁

    另外,可以查看这篇优秀的回答


    刷新令牌不是关于什么?

    通过刷新令牌更新/撤销访问级别是使用刷新令牌的副产品,否则,在到期并取得新令牌时,可以撤销单独的访问令牌或修改其访问级别


    13
    攻击者为什么更难获得刷新令牌而非访问令牌?使用HTTPS可在传输过程中提供保护,但我需要在浏览器中存储两者以防单页应用。因此,攻击者可以窃取它们。我有什么遗漏吗? - Vmxes
    2
    从上下文来看,我认为它们没有区别。但是从浏览器、路由器、ISP、VPN等之间的过渡层的上下文来看,访问令牌可以每小时传递1000次,而刷新令牌只传递一次。 - mfaani
    1
    在你的例子中,你使用旧令牌(旧密钥)来获取新令牌(新密钥)。这不是获取访问令牌的方式,没有刷新令牌机制:每次需要新的访问令牌时,你必须发送回你的密码。 - XouDo
    1
    同时拥有访问令牌和刷新令牌意味着攻击者有更多机会访问用户账户,因为攻击者可以猜测其中一个令牌。如果不是这样,那么如果攻击者能够从客户端获取访问令牌,那么为什么他/她不能黑掉您的刷新令牌呢? - Ulvi
    2
    攻击者可以访问访问令牌和刷新令牌。关键是,使用长期的访问令牌,受害者和攻击者都可以继续操作。然而,使用短期的访问令牌和刷新令牌轮换,一旦刷新令牌被使用两次,刷新令牌将停止运行,双方都将失去访问权限。 - Veita
    显示剩余6条评论

    43

    这篇回答来自Justin Richer通过OAuth 2标准邮件列表。已获得他的许可发布。


    刷新令牌的生命周期取决于授权服务器(AS)-它们可以过期、被撤销等。刷新令牌和访问令牌之间的区别在于受众:刷新令牌只返回到授权服务器,而访问令牌则返回到资源服务器(RS)。

    此外,仅仅获取访问令牌并不意味着用户已登录。实际上,用户可能已经退出了登录,这实际上是刷新令牌的预期用例。刷新访问令牌将让您以用户的名义访问API,但它不会告诉您用户是否仍然在线。

    OpenID Connect不仅从访问令牌中提供用户信息,还为您提供了一个ID令牌。这是一个针对客户端本身而不是AS或RS的单独数据。在OIDC中,只有在您可以获得新的ID令牌时才应该考虑某人实际上“已登录”协议。仅仅刷新它很可能不够。

    有关更多信息,请阅读http://oauth.net/articles/authentication/


    2
    这似乎涉及到OpenID Connect和身份验证,所以我不明白这如何回答问题,问题是关于令牌刷新的动机。 - sleske

    20

    客户端可以通过多种方式被攻击,例如手机可以被克隆。访问令牌过期意味着客户端必须重新向授权服务器进行身份验证。在重新身份验证期间,授权服务器可以检查其他特征(即执行自适应访问管理)。

    刷新令牌允许客户端仅进行重新身份验证,而重新授权则强制要求与用户对话,许多人表示他们不愿意这样做。

    刷新令牌基本上与普通网站可能会选择每隔一小时左右定期重新验证用��(例如银行网站)的位置相同。目前它并没有得到广泛使用,因为大多数社交网站不会重新验证Web用户,那么他们为什么会重新验证客户端呢?


    4
    “刷新令牌允许客户端进行重新认证…” 这是一个重要的方面。 - James

    16

    更简单地说,当你不想让用户再次输入凭据,但仍然希望有能力撤销权限(通过撤销刷新令牌)时,请使用刷新令牌。

    你无法撤销访问令牌,只能撤销刷新令牌。


    1
    您可以撤销访问令牌,这将要求再次登录以获得另一个访问令牌,或者使用刷新令牌获取另一个访问令牌。如果刷新令牌无效,则用户必须重新进行身份验证才能获取新的访问令牌和新的刷新令牌。 - Atieh
    12
    我不同意。访问令牌是由认证服务器颁发的,带有过期日期的签名后发送给客户端。当客户端将此令牌发送到资源服务器时,资源服务器不会联系认证服务器来验证令牌;它只查看(已签名且未被篡改的)令牌中的过期日期。因此,无论您在认证服务器上做些什么来尝试“撤销”,资源服务器都不关心。有些人将客户端注销称为撤销(即客户端删除其令牌),但在我看来,这是误导性的术语 - 我们想要在服务器上“撤销”令牌,而不是客户端。 - bitcoder
    1
    并不是说您不能编写自定义代码来忽略某些令牌(例如在此处https://dev59.com/82Eh5IYBdhLWcg3wMA0t),但这样做可能涉及每次客户端调用时从资源服务器到oauth服务器/数据库的一些网络请求。相反,使用刷新令牌可以避免这些调用,并且我认为它更符合oauth作者的意图。 - bitcoder

    14
    为什么不直接让 access_token 的持续时间和 refresh_token 一样长,而不是使用 refresh_token 呢?
    除了其他人提供的优秀答案之外,还有另一个原因我们会使用 refresh token,这与声明有关。
    每个令牌都包含声明,可以包括用户的名称、角色或创建声明的提供者等任何内容。当令牌被刷新时,这些声明会被更新。
    如果我们更频繁地刷新令牌,显然会对身份服务造成更大的压力; 然而,我们会得到更准确和最新的声明。

    4
    将这样的“声明”放进访问令牌中是一种不寻常的不良做法。正如RFC6749规范中所述,访问令牌“通常对客户端不透明”。您有OAuth提供程序的示例吗? - Kevin Christopher Henry
    4
    当用户角色从管理员降级为普通用户时,期望用户角色立即被撤销而不是在访问令牌过期时。因此,每次请求都需要访问数据库似乎是不可避免的。 - svlada
    2
    @svlada 我想这种情况下,应用程序将实体从 ADMIN 降级为 REGULAR_USER 时(在相同的流程中),也需要撤销相应的令牌。也就是说,如果我们知道要更改声明,我们不会等待过期,而是立即撤销。 - e_i_pi

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接