OAuth 2.0协议草案的第4.2节指出,授权服务器可以返回access_token(用于通过资源验证自己)和refresh_token,refresh_taken纯粹用于创建新的access_token:

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

为什么两者都有?为什么不让access_token和refresh_token一样长,而不设置refresh_taken?


当前回答

Catchdave提供的讨论链接还有Dick Hardt提出的另一个有效的观点(原始的,死链接),我认为除了上面写的内容之外,这里还值得一提:

我记得刷新令牌是为了安全和撤销。<...>撤销:如果访问令牌是自包含的,则可以通过不发布新的访问令牌来撤销授权。资源不需要查询授权服务器以查看访问令牌是否有效。这简化了访问令牌验证,并使扩展和支持多个授权服务器更加容易。当访问令牌有效但授权被撤销时,有一个时间窗口。

事实上,在资源服务器和授权服务器是同一个实体的情况下,并且用户和其中任何一个之间的连接(通常)都是同样安全的,那么将刷新令牌与访问令牌分开并没有多大意义。

尽管如引用中所述,刷新令牌的另一个作用是确保用户可以随时(例如通过其配置文件中的web界面)撤销访问令牌,同时保持系统的可伸缩性。

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

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

服务器允许客户端通过发出令牌访问预定义范围内的用户数据。由于我们希望保持令牌的可撤销性,我们必须在数据库中存储令牌以及设置或取消设置的标志“已撤销”(否则,如何使用自包含令牌?)。数据库可以包含多达len(用户)x len(注册客户端)x len(范围组合)的记录。然后,每个API请求都必须命中数据库。虽然对执行O(1)的数据库进行查询非常简单,但单点故障本身可能会对系统的可伸缩性和性能产生负面影响。

具有长期刷新令牌和短期访问令牌的系统应该如何工作

在这里,我们发布两个密钥:带有数据库中相应记录的随机刷新令牌,以及签名的自包含访问令牌,其中包含过期时间戳字段。

由于访问令牌是自包含的,因此我们根本不必访问数据库来检查其有效性。我们所要做的就是解码令牌并验证签名和时间戳。

尽管如此,我们仍然需要保留刷新令牌的数据库,但对该数据库的请求数量通常由访问令牌的寿命定义(寿命越长,访问率越低)。

为了撤销特定用户对客户端的访问,我们应该将相应的刷新令牌标记为“已撤销”(或完全删除),并停止发布新的访问令牌。很明显,在一个窗口中,刷新令牌已被撤销,但其访问令牌可能仍然有效。

权衡取舍

刷新令牌部分消除了访问令牌数据库的SPoF(单点故障),但它们有一些明显的缺点。

“窗口”。事件“用户撤销访问”和“保证撤销访问”之间的时间间隔。客户端逻辑的复杂性。无刷新令牌使用访问令牌发送API请求如果访问令牌无效,则失败并要求用户重新验证带刷新令牌使用访问令牌发送API请求如果访问令牌无效,请尝试使用刷新令牌更新它如果刷新请求通过,则更新访问令牌并重新发送初始API请求如果刷新请求失败,请要求用户重新验证

我希望这个答案确实有意义,有助于某人做出更深思熟虑的决定。我还想指出,一些著名的OAuth2提供商,包括github和foursquare,采用了没有刷新令牌的协议,并对此感到满意。

其他回答

假设您使access_token持续很长时间,并且没有refresh_token,那么在一天之内,黑客就可以访问所有受保护的资源!

但如果你有refresh_token,access_token的生存时间很短,所以黑客很难破解你的access_toke,因为它在短时间后将无效。Access_token只能通过不仅使用refresh_token,而且还可以通过client_id和client_secret(黑客没有)检索回来。

刷新令牌的思想是,如果访问令牌因其短暂而受到破坏,攻击者可以在有限的窗口中滥用它。

刷新令牌(如果被破坏)是无用的,因为攻击者除了需要刷新令牌之外还需要客户端id和密码,以便获得访问令牌。

话虽如此,由于每次对授权服务器和资源服务器的调用都是通过SSL完成的,包括请求访问/刷新令牌时的原始客户端id和secret,因此我不确定访问令牌如何比长期刷新令牌和客户端id/secret组合更“不可妥协”。

当然,这与不同时控制授权服务器和资源服务器的实现不同。

这里有一个很好的线程,讨论了刷新令牌的使用:OAuth存档。

引用上述内容,讨论刷新令牌的安全目的:

刷新令牌。。。降低长期access_token泄漏的风险(在不安全的资源服务器上的日志文件中查询参数,测试版或编码不良的资源服务器应用程序,非https站点上的JS SDK客户端将access_toke放入cookie等)

为什么不让access_token和refresh_token一样长不吃点心吗?

除了其他人提供的出色答案之外,我们使用刷新令牌还有另一个原因,这与声明有关。

每个令牌都包含声明,这些声明可以包括用户名、用户角色或创建声明的提供者等任何内容。当刷新令牌时,这些声明将被更新。

如果我们更频繁地刷新令牌,显然会给我们的身份服务带来更大的压力;然而,我们正在获得更准确和最新的声明。

这一切都与扩展和保持资源服务器无状态有关。

您的服务器/资源服务器服务器是无状态的,这意味着不检查任何存储以快速响应。通过使用公钥验证令牌的签名来执行此操作。检查每个请求的access_token。通过只检查access_token的签名和过期日期,响应速度非常快,并允许扩展。access_token应该有很短的到期时间(几分钟),因为如果它被泄露,就没有办法撤销它。身份验证服务器/OAuth服务器服务器不是无状态的,但它可以,因为请求要少得多。仅在access_token过期时检查refresh_token。(例如每2分钟)请求速率远低于资源服务器。将刷新令牌存储在数据库中,并可以撤消它。refresh_token可能有很长的到期时间(几周/几个月),如果它被泄露,有办法撤销它。

不过有一个重要的注意事项,身份验证服务器的请求要少得多,因此可以处理负载,但是由于必须存储所有refresh_token,因此可能存在存储问题,如果用户急剧增加,这可能会成为一个问题。

尽管上面的答案很好,但我作为一名安全硕士生和程序员,在研究买家保护和欺诈时,曾在eBay工作过,可以说,将访问令牌和刷新令牌分开,在骚扰频繁输入用户名/密码的用户和保留撤销对可能滥用您服务的访问权限之间取得了最佳平衡。

想象一下这样的情景。您向用户发出3600秒的访问令牌,刷新令牌的时间长达一天。

用户是一个好用户,他在家里,上下你的网站,在他的iPhone上购物和搜索。他的IP地址不会改变,并且服务器负载很低。像每分钟3-5页的请求。当他在访问令牌上的3600秒结束时,他需要一个具有刷新令牌的新令牌。在服务器端,我们检查他的活动历史和IP地址,认为他是一个人,行为举止得体。我们授予他一个新的访问令牌以继续使用我们的服务。用户不需要再次输入用户名/密码,直到达到刷新令牌本身的一天寿命。用户是一个粗心大意的用户。他住在美国纽约,病毒程序被关闭,在波兰被黑客入侵。当黑客获得访问令牌和刷新令牌时,他试图模拟用户并使用我们的服务。但是,在短暂的实时访问令牌过期后,当黑客试图刷新访问令牌时,我们在服务器上注意到用户行为历史中的IP发生了巨大变化(嘿,这家伙在美国登录,现在在波兰仅3600秒后刷新访问)。我们终止刷新过程,使刷新令牌本身无效,并提示再次输入用户名/密码。该用户是恶意用户。他打算通过使用机器人每分钟调用1000次我们的API来滥用我们的服务。直到3600秒后,当他试图刷新访问令牌时,我们注意到他的行为,认为他可能不是人类。我们拒绝并终止刷新过程,并要求他再次输入用户名/密码。这可能会破坏他的机器人的自动流动。至少让他不舒服。

当我们试图平衡我们的工作、用户体验和被盗令牌的潜在风险时,您可以看到刷新令牌的表现非常完美。服务器端的看门狗不仅可以检查IP更改,还可以检查api调用的频率,以确定用户是否应该是一个好用户。

另一个词是,您还可以尝试通过在每个api调用上实现基本IP看门狗或任何其他措施来限制被盗令牌/滥用服务的损害控制。但这很昂贵,因为您必须读取和写入有关用户的记录,并且会降低服务器响应速度。