OAuth 2.0 协议草案的第 4.2 节表明,授权服务器可以返回一个access_token
(用于对资源进行身份验证)和一个refresh_token
,它纯粹用于创建一个新的access_token
:
https://www.rfc-editor.org/rfc/rfc6749#section-4.2
为什么两者都有?为什么不只做access_token
最后一个只要 therefresh_token
并且没有 arefresh_token
呢?
OAuth 2.0 协议草案的第 4.2 节表明,授权服务器可以返回一个access_token
(用于对资源进行身份验证)和一个refresh_token
,它纯粹用于创建一个新的access_token
:
https://www.rfc-editor.org/rfc/rfc6749#section-4.2
为什么两者都有?为什么不只做access_token
最后一个只要 therefresh_token
并且没有 arefresh_token
呢?
Catchdave 提供的讨论链接还有 Dick Hardt 提出的另一个有效点 (原始的死链接) ,我认为除了上面写的内容之外,这里值得一提:
我对刷新令牌的回忆是为了安全和撤销。<...>
撤销:如果访问令牌是自包含的,则可以通过不发布新的访问令牌来撤销授权。资源不需要查询授权服务器来查看访问令牌是否有效。这简化了访问令牌验证,并且更容易扩展和支持多个授权服务器。当访问令牌有效但授权被撤销时,存在一个时间窗口。
实际上,在资源服务器和授权服务器是同一个实体的情况下,并且用户和它们之间的连接(通常)同样安全,将刷新令牌与访问令牌分开没有多大意义。
尽管如引用中所述,刷新令牌的另一个作用是确保用户可以随时撤销访问令牌(例如,通过其配置文件中的 Web 界面),同时保持系统可扩展性.
通常,令牌可以是指向服务器数据库中特定记录的随机标识符,也可以是它们本身包含所有信息(当然,必须使用MAC对这些信息进行签名)。
具有长期访问令牌的系统应该如何工作
服务器允许客户端通过发出令牌来访问预定义范围内的用户数据。由于我们希望保持令牌可撤销,我们必须将令牌与设置或取消设置的标志“已撤销”一起存储在数据库中(否则,你将如何使用自包含令牌来做到这一点?)数据库可以包含尽可能多的len(users) x len(registered clients) x len(scopes combination)
记录. 然后,每个 API 请求都必须访问数据库。尽管执行 O(1) 对此类数据库进行查询非常简单,但单点故障本身会对系统的可伸缩性和性能产生负面影响。
具有长期刷新令牌和短期访问令牌的系统应该如何工作
这里我们发出两个密钥:随机刷新令牌与数据库中的相应记录,以及签名的自包含访问令牌,其中包含过期时间戳字段。
由于访问令牌是自包含的,我们根本不需要访问数据库来检查其有效性。我们所要做的就是解码令牌并验证签名和时间戳。
尽管如此,我们仍然需要保留刷新令牌的数据库,但是对这个数据库的请求次数通常由访问令牌的生命周期定义(生命周期越长,访问率越低)。
为了撤销特定用户对 Client 的访问权限,我们应该将相应的刷新令牌标记为“已撤销”(或完全删除)并停止发布新的访问令牌。很明显,虽然有一个窗口在此期间刷新令牌已被撤销,但其访问令牌可能仍然有效。
权衡取舍
刷新令牌部分消除了 Access Token 数据库的 SPoF(单点故障),但它们也有一些明显的缺点。
窗户”。事件“用户撤销访问”和“访问被保证被撤销”之间的时间范围。
客户端逻辑的复杂性。
没有刷新令牌
带刷新令牌
我希望这个答案确实有意义,并帮助某人做出更深思熟虑的决定。我还想指出,一些著名的 OAuth2 提供者,包括 github 和foursquare 采用了没有刷新令牌的协议,并且似乎对此很满意。
刷新令牌的想法是,如果访问令牌被泄露,因为它是短暂的,攻击者有一个有限的窗口来滥用它。
刷新令牌,如果被泄露,将毫无用处,因为攻击者除了刷新令牌之外还需要客户端 ID 和密码才能获得访问令牌。
话虽如此,因为对授权服务器和资源服务器的每次调用都是通过 SSL 完成的——包括当他们请求访问/刷新令牌时的原始客户端 id 和秘密——我不确定访问令牌是怎样的。比长期刷新令牌和客户端ID/秘密组合更容易妥协”。
这当然与您不控制授权和资源服务器的实现不同。
这是一个讨论刷新令牌使用的好帖子:OAuth Archives。
上面的引用,谈论刷新令牌的安全目的:
刷新令牌...降低长期 access_token 泄漏的风险(在不安全的资源服务器、测试版或编码不佳的资源服务器应用程序上的日志文件中查询参数、将 access_token 放在非 https 站点上的 JS SDK 客户端饼干等)
尽管上面有所有很好的答案,但我作为一名安全硕士生和程序员,曾在 eBay 工作过,当时我研究了买家保护和欺诈问题,可以说分离访问令牌和刷新令牌在骚扰频繁使用用户名的用户之间取得了最佳平衡/password 输入并保持授权以撤销对潜在滥用服务的访问权限。
想想这样的场景。您发出一个 3600 秒的访问令牌的用户,并且刷新令牌的时间要长得多一天。
用户是一个很好的用户,他在家并且在他的 iPhone 上打开/关闭您的网站购物和搜索。他的 IP 地址不会改变,并且在您的服务器上的负载非常低。就像每分钟 3-5 个页面请求一样。当他在访问令牌上的 3600 秒结束时,他需要一个带有刷新令牌的新令牌。我们在服务器端检查他的活动历史和 IP 地址,认为他是一个人并且表现自己。我们授予他新的访问令牌以继续使用我们的服务。用户无需再次输入用户名/密码,直到他达到刷新令牌本身的一天生命周期。
用户是粗心的用户。他住在美国纽约,他的病毒程序被关闭,并在波兰被黑客入侵。当黑客获得访问令牌和刷新令牌时,他试图冒充用户并使用我们的服务。但是在短期访问令牌过期后,当黑客尝试刷新访问令牌时,我们在服务器上注意到用户行为历史中的 IP 发生了巨大变化(嘿,这家伙在美国登录,现在在波兰刷新访问仅 3600 秒后 ???)。我们终止刷新过程,使刷新令牌本身无效并提示再次输入用户名/密码。
该用户是恶意用户。他打算通过使用机器人每分钟调用 1000 次我们的 API 来滥用我们的服务。他可以很好地做到这一点,直到 3600 秒后,当他尝试刷新访问令牌时,我们注意到他的行为并认为他可能不是人类。我们拒绝并终止刷新过程,并要求他再次输入用户名/密码。这可能会破坏他的机器人的自动流程。至少让他不舒服。
当我们尝试平衡我们的工作、用户体验和令牌被盗的潜在风险时,您可以看到刷新令牌的效果非常好。服务器端的看门狗可以检查IP变化,api调用频率,以确定用户是否为好用户。
换句话说,您还可以尝试通过在每个 api 调用上实施基本 IP 看门狗或任何其他措施来限制被盗令牌/滥用服务的损害控制。但这很昂贵,因为您必须读取和写入有关用户的记录,并且会减慢服务器的响应速度。
这些答案都没有解决刷新令牌存在的核心原因。显然,您始终可以通过将客户端凭据发送到身份验证服务器来获得新的访问令牌/刷新令牌对 - 这就是您首先获取它们的方式。
因此,刷新令牌的唯一目的是限制使用通过线路发送到身份验证服务的客户端凭据。访问令牌的TTL越短,必须使用客户端凭据获取新访问令牌的次数就越多,因此攻击者破坏客户端凭据的机会就越大(尽管这可能非常困难,如果非对称加密被用于发送它们)。因此,如果您有一次性刷新令牌,则可以在不影响客户端凭据的情况下将访问令牌的 TTL 设置为任意小。
为了消除一些混乱,您必须了解客户端密码和用户密码的角色,它们是非常不同的。
客户端是由服务器支持的应用程序/网站/程序/...,它希望通过使用第三方身份验证服务来对用户进行身份验证。客户端密码是一个(随机)字符串,此客户端和身份验证服务器都知道。使用这个秘密,客户端可以通过身份验证服务器识别自己,接收请求访问令牌的授权。
要获取初始访问令牌和刷新令牌,需要的是:
然而,为了获得刷新的访问令牌,客户端使用以下信息:
这清楚地表明了区别:刷新时,客户端通过使用其客户端密钥接收刷新访问令牌的授权,因此可以使用刷新令牌而不是用户 ID + 密码重新验证用户。这有效地防止了用户必须重新输入他/她的密码。
这也表明丢失刷新令牌是没有问题的,因为客户端 ID 和机密是未知的。它还表明,保持客户端 ID 和客户端机密是至关重要的。
这个答案是在两位高级开发人员(John Brayton 和 David Jennes)的帮助下完成的。
使用刷新令牌的主要原因是减少攻击面。
假设没有刷新键,让我们看一下这个例子:
一栋楼有80扇门。所有的门都是用同一把钥匙打开的。密钥每 30 分钟更换一次。在 30 分钟结束时,我必须将旧钥匙交给钥匙制造商并获得一把新钥匙。
如果我是黑客并拿到了你的钥匙,那么在 30 分钟结束时,我会将它快递给钥匙制造商并获得一个新钥匙。我将能够不断地打开所有的门,不管钥匙怎么换。
问题:在这 30 分钟内,我有多少次攻击钥匙的机会?每次您使用密钥时,我都有 80 次黑客攻击机会(将其视为发出网络请求并传递访问令牌以识别您自己)。这就是 80X 攻击面。
现在让我们看一下相同的示例,但这次我们假设有一个刷新键。
一栋楼有80扇门。所有的门都是用同一把钥匙打开的。密钥每 30 分钟更换一次。要获取新密钥,我无法传递旧的访问令牌。我必须只通过刷新键。
如果我是黑客并拿到了你的密钥,我可以使用它 30 分钟,但在 30 分钟结束时将它发送给密钥制造者没有任何价值。如果我这样做了,那么密钥制造者只会说“此令牌已过期。您需要刷新令牌。” 为了能够扩展我的破解,我必须将信使破解给钥匙制造商。信使有一个独特的密钥(将其视为刷新令牌)。
问题:在这 30 分钟内,我有多少次针对刷新键的黑客攻击机会?80?不,我只有 1 次黑客攻击机会。在此期间,快递员与钥匙制造商沟通。这就是 1X 攻击面。我确实有 80 次攻击密钥的机会,但 30 分钟后它们都不好。
服务器将根据凭据和(通常)JWT 的签名来验证访问令牌。
访问令牌泄漏是不好的,但一旦过期,它对攻击者就不再有用了。刷新令牌泄漏要糟糕得多,但可能不太可能。(我认为存在质疑刷新令牌泄漏的可能性是否远低于访问令牌泄漏的可能性,但这就是想法。)
关键是访问令牌会添加到您发出的每个请求中,而刷新令牌仅在刷新流程期间使用,因此 MITM 看到令牌的机会较小
频率可以帮助攻击者。SSL 中类似 Heartbleed的潜在安全漏洞、客户端中的潜在安全漏洞以及服务器中的潜在安全漏洞都使泄漏成为可能。
此外,如果授权服务器与处理其他客户端请求的应用程序服务器分开,那么该应用程序服务器将永远不会看到刷新令牌。它只会看到不会存在更长时间的访问令牌。
分隔有利于安全。
最后但并非最不重要的刷新令牌可以得到旋转。意思是“每次客户端请求将刷新令牌交换为新的访问令牌时,都会返回一个新的刷新令牌。”。随着刷新令牌的不断交换和失效,威胁降低了。举个例子:令牌通常在 TTL 通常一个小时后过期。
刷新令牌并非总是如此,但经常在使用时被撤销并发行新令牌。这意味着如果您遇到网络故障,当您检索新的刷新令牌时,下次发送该刷新令牌时,它会被视为已撤销,您必须登录。
所有这些都有助于减轻威胁
有关此问题的另一种看法,请参见这个很棒的答案
什么刷新令牌不是关于?
通过刷新令牌更新/撤销访问级别的能力是选择使用刷新令牌的副产品,否则独立访问令牌可能会被撤销或在其过期并且用户获得新令牌时修改其访问级别
此答案来自 Justin Richer 通过 OAuth 2 标准正文电子邮件列表。这是在他的许可下发布的。
刷新令牌的生命周期取决于(AS)授权服务器——它们可以过期、被撤销等。刷新令牌和访问令牌之间的区别在于受众:刷新令牌只返回到授权服务器,访问令牌转到(RS)资源服务器。
此外,仅获取访问令牌并不意味着用户已登录。事实上,用户甚至可能不再存在,这实际上是刷新令牌的预期用例。刷新访问令牌将使您代表用户访问 API,它不会告诉您用户是否在那里。
OpenID Connect 不仅为您提供来自访问令牌的用户信息,它还为您提供 ID 令牌。这是针对客户端本身而不是 AS 或 RS 的单独数据片段。在 OIDC 中,如果您可以获得新的 ID 令牌,您应该只考虑某人实际通过协议“登录”。刷新它可能还不够。
客户端可能会以多种方式受到损害。例如,可以克隆手机。访问令牌过期意味着客户端被迫重新向授权服务器进行身份验证。在重新认证期间,授权服务器可以检查其他特性(IOW 执行自适应访问管理)。
刷新令牌只允许客户端重新认证,而重新授权强制与用户进行对话,许多人表示他们不愿意这样做。
刷新令牌基本上适用于普通网站可能会选择在一小时左右后定期重新验证用户的地方(例如银行网站)。由于大多数社交网站不会重新验证 Web 用户,因此目前使用率不高,那么他们为什么要重新验证客户端呢?
为了进一步简化 B T 的回答:当您通常不希望用户必须再次输入凭据但仍希望能够撤销权限时使用刷新令牌(通过撤销刷新令牌)
您不能撤销访问令牌,只能撤销刷新令牌。
为什么不让 access_token 与 refresh_token 一样长而没有 refresh_token 呢?
除了其他人提供的很好的答案之外,我们使用刷新令牌还有另一个原因,它与声明有关。
每个令牌都包含声明,其中可以包括用户名、他们的角色或创建声明的提供者等任何内容。随着令牌的刷新,这些声明也会更新。
如果我们更频繁地刷新令牌,我们显然会给我们的身份服务带来更大的压力;但是,我们正在获得更准确和最新的声明。
假设你做的access_token
最后一个很长,并且没有refresh_token
,所以在一天之内,黑客得到了这个access_token
,他可以访问所有受保护的资源!
但是如果你有refresh_token
,那么它access_token
的存活时间很短,所以黑客很难破解你的access_token
,因为它会在短时间内失效。
只能通过使用 not only也可以通过andAccess_token
来取回,而黑客没有。refresh_token
client_id
client_secret
授权服务器保留刷新令牌。访问令牌是自包含的,因此资源服务器可以在不存储它的情况下对其进行验证,从而在验证时节省检索工作。讨论中缺少的另一点来自 rfc6749#page-55
“例如,授权服务器可以使用刷新令牌轮换,其中每个访问令牌刷新响应都会发出一个新的刷新令牌。之前的刷新令牌无效但由授权服务器保留。如果刷新令牌被泄露并随后被攻击者和合法客户端,其中之一将提供一个无效的刷新令牌,这将通知授权服务器违规行为。”
我认为使用刷新令牌的全部意义在于,即使攻击者以某种方式设法获得刷新令牌、客户端 ID 和秘密组合。如果每个刷新请求都导致新的访问令牌和刷新令牌,则可以跟踪从攻击者获取新访问令牌的后续调用。
让我们考虑一个系统,其中每个用户都链接到一个或多个角色,每个角色都链接到一个或多个访问权限。可以缓存此信息以获得更好的 API 性能。但是,用户和角色配置可能会发生变化(例如,可能会授予新访问权限或撤销当前访问权限),这些应该反映在缓存中。
我们可以为此目的使用访问和刷新令牌。当使用访问令牌调用 API 时,资源服务器会检查缓存的访问权限。如果有任何新的访问授权,它不会立即反映。一旦访问令牌过期(比如 30 分钟后)并且客户端使用刷新令牌生成新的访问令牌,就可以使用来自数据库的更新的用户访问权限信息来更新缓存。
换句话说,我们可以将昂贵的操作从使用访问令牌的每个 API 调用转移到使用刷新令牌生成访问令牌的事件中。
刷新令牌和访问令牌只是术语。
这个小类比有助于巩固使用访问令牌和刷新令牌背后的基本原理:
假设Alice通过邮寄方式向 Bob发送了一张支票,该支票可以在发行后 1 小时(假设)内兑现,否则银行将不会兑现。但是爱丽丝还在帖子中附上了一张给银行的便条,要求银行接受并兑现支票,以防它有点延迟(在规定的范围内)
当Bob收到这张支票时,如果他看到这张支票被篡改(token tampering),他将自己丢弃这张支票。如果没有,他可以把它带到银行兑现。在这里,当银行注意到发行时间已超过 1 小时的时限,但看到 Alice 签名的票据要求银行兑现,以防规定的延迟在可接受的范围内。
看到这张纸条后,银行会尝试验证签名的消息并检查 Alice 是否仍然拥有正确的权限。如果是,银行将支票兑现。Bob 现在可以向 Alice 确认这一点。
虽然不是非常准确,但这个类比可以帮助您注意处理交易所涉及的不同部分:
主要是,我们希望减少对 Auth Server 以及最终对数据库的 API 调用次数,以优化可扩展性。我们需要在便利性和安全性之间取得适当的平衡。
注意:在链中让 Auth 服务器比资源服务器更早地响应请求当然更常见。
首先,客户端通过授权授权向授权服务器进行身份验证。
然后,客户端通过提供访问令牌向资源服务器请求受保护的资源。
资源服务器验证访问令牌并提供受保护的资源。
客户端通过授予访问令牌向资源服务器发出受保护的资源请求,资源服务器在该令牌中对其进行验证并服务于请求(如果有效)。此步骤不断重复,直到访问令牌过期。
如果访问令牌过期,客户端向授权服务器进行身份验证,并通过提供刷新令牌来请求新的访问令牌。如果访问令牌无效,则资源服务器将无效令牌错误响应发送回客户端。
客户端通过授予刷新令牌与授权服务器进行身份验证。
然后,授权服务器通过对客户端进行身份验证来验证刷新令牌,并在其有效时发出新的访问令牌。
据我了解,如果您需要能够撤消访问权限,刷新令牌只是为了节省性能和成本。
例如1:不实现刷新令牌;只实现长期访问令牌:如果用户滥用服务(例如:不支付订阅费),您需要能够撤销访问令牌=>您需要在每个 API 调用上检查访问令牌的有效性需要访问令牌,这会很慢,因为它需要数据库查找(缓存可以提供帮助,但这更复杂)。
例如 2:实现刷新令牌和短期访问令牌:如果用户滥用服务(例如:不支付订阅费),您需要能够撤销访问令牌 => 短期访问令牌将在短期后过期白色(例如 1 小时)并且用户将需要获取新的访问令牌,因此我们不需要对需要访问令牌的每个 API 调用进行验证。您只需要在从刷新令牌生成访问令牌时验证用户。对于不良用户,如果无法生成访问令牌,您可以注销该用户。当用户尝试重新登录时,验证将再次运行并返回错误。
由于刷新和访问令牌是包含大量语义的术语,因此术语转换可能会有所帮助吗?
在 2020 年,刷新令牌也可以存在于浏览器中(最初是为后端系统提供的)被接受 - 请参阅https://pragmaticwebsecurity.com/articles/oauthoidc/refresh-token-protection-implications。因此,焦点从“可刷新性”(在没有用户的情况下后端如何延长对 api 的访问)切换到“可撤销性”。
因此,对我来说,将刷新令牌读取为可撤销令牌并将访问令牌读取为不可撤销令牌(可能是快速过期不可撤销令牌)看起来更安全。
作为关于 2021 年良好实践的旁注,系统始终可以从可撤销令牌开始,并在授权服务器压力增加时转向不可撤销令牌。