刷新令牌在客户端上的哪里存储?

时间:2019-08-26 00:34:31

标签: authentication cookies oauth oauth-2.0 token

我的SPA应用程序使用以下架构(source):

enter image description here

这假定我的客户端应用程序知道刷新令牌,因为如果不存在用户凭据(例如电子邮件/密码),我需要它来请求新的访问令牌。

我的问题:我将刷新令牌存储在客户端应用程序中的什么位置?关于SO上有关此主题的问题/答案很多,但是关于刷新令牌的答案尚不清楚

访问令牌和刷新令牌不应存储在本地/会话存储中,因为它们不是存放任何敏感数据的地方。因此,我会将访问令牌存储在httpOnly cookie中(即使存在CSRF),并且无论如何我对资源服务器的大多数请求都需要它。

但是刷新令牌怎么办?,我不能将其存储在cookie中,因为(1)它将随每个请求一起发送到我的资源服务器,这也使它容易受到CSRF的攻击,并且(2)它会发送具有相同攻击向量的暴露访问/刷新令牌。

我可以想到三种解决方案:


1)将刷新令牌存储在内存中的JavaScript变量中,这有两个缺点:

  • a)它容易受到XSS的攻击(但可能不如本地/会话存储那么明显
  • b)如果用户关闭浏览器标签,则会释放“会话”

尤其是后一个缺点使它最终成为不良的UX。


2)将访问令牌存储在会话存储中,并通过Bearer access_token授权标头将其发送到我的资源服务器。然后,我可以使用httpOnly cookie作为刷新令牌。我有一个可以想到的缺点:

  • a)刷新令牌随对资源服务器的每个请求公开给CSRF。

3)将两个令牌都保存在httpOnly cookie中,这具有上述缺点,即两个令牌都暴露于相同的攻击向量。


也许除了我提到的缺点之外还有其他方法或更多(请让我知道),但是最后一切归结为我在哪里将刷新令牌保留在客户端上?是httpOnly Cookie还是内存中的JS变量?如果是前者,那么我应该在哪里放置访问令牌?

很高兴能从熟悉该主题的人那里获得有关如何以最佳方式进行操作的任何线索。

6 个答案:

答案 0 :(得分:5)

您可以将令牌(访问和刷新)都存储为cookie。但是刷新令牌必须具有特殊的路径(例如/ refresh)。因此,刷新令牌将仅发送给/ refresh url的请求,而不会发送给访问令牌之类的每个请求。

答案 1 :(得分:1)

您没有使用最佳的身份验证体系结构。 SPA是公共客户端,因此无法安全地存储诸如客户端密钥或刷新令牌之类的信息。您应该切换到Implicit Flow,其中refresh tokens are not used。但是可以使用Silent Authentication(静默更新)。

我建议使用OIDC certified library,其中所有已经针对SPA应用程序进行了排序。我最喜欢的一个:https://github.com/damienbod/angular-auth-oidc-client

答案 2 :(得分:1)

您可以将加密令牌安全地存储在HttpOnly cookie中。

https://medium.com/@sadnub/simple-and-secure-api-authentication-for-spas-e46bcea592ad

如果您担心长寿的刷新令牌。您可以跳过存储而完全不使用它。只需将Access Token保留在内存中,并在Access Token过期时进行静默登录即可。

  

不要使用Implicit流,因为它是obsolete

SPA的最安全的身份验证方法是Authorization Code with PKCE

通常,最好使用基于oidc-client的现有库,而不是自己构建一些库。

答案 3 :(得分:0)

  

OAuth定义了四种授权类型:授权代码,隐式,      资源所有者密码凭据和客户端凭据。它也是      提供了用于定义其他授权类型的扩展机制。

__ RFC 6749 - The OAuth 2.0 Authorization Framework


Authorization Code进程本质上被设计为与安全客户端一起使用,例如。服务器,其保护足以将Client Secret容纳在其中。如果您的客户端足够安全,可以保守秘密,只需将Refresh TokenClient Secret放在相同的安全存储中。

User-Agent(UA)中托管的应用程序不是这种情况。对于那些规范,建议使用Implicit授予类型,该授予类型在Access Token符号后的片段中的Redirection URI之后显示#。鉴于您是直接在User-Agent中接收令牌的,因此它本质上是一种不安全的方法,除了遵循User-Agent的安全规则外,您无能为力。

您可以将应用程序的使用限制为特定的用户代理,但是很容易被篡改。您可以将令牌存储在cookie中,但是如果UA不遵守通用安全规范,也可以访问令牌。如果令牌是由UA实施和提供的,则可以将其存储在本地存储中,如果它遵守规范,则可以再次存储。

这些隐式间接授权的关键是对UA的信任。否则,最安全的授予类型是授权码,因为它要求在受控环境(应用程序的服务器)上安全可靠地存储秘密。

如果您别无选择,只能使用隐式调用,那就勇往直前,并相信用户使用遵循安全协议的安全UA;您不必为用户对UA选择不当承担任何责任。

答案 4 :(得分:0)

将访问令牌存储在会话存储中,并通过 我的资源服务器的承载access_token授权头。然后 我可以将httpOnly cookie用于刷新令牌。这个有一个 我能想到的缺点: a)刷新令牌在对资源服务器发出的每个请求中都向CSRF公开。

您可以正确设置CORS policy,以便仅从授权服务器接受对/refresh_token的请求。

如果客户端和服务器是从同一台计算机提供的,则可以在sameSite中将标志Cookie设置为true,并包含一个anti-CSRF令牌。

答案 5 :(得分:0)

如果您的身份验证提供程序实现了刷新令牌轮换,您可以将它们存储在本地存储中。

但这意味着每次客户端刷新 JWT 时,您的身份验证提供程序都应返回一个新的刷新令牌。如果尝试第二次使用一个刷新令牌,它还应该有一种使后代刷新令牌无效的方法。

https://auth0.com/docs/tokens/refresh-tokens/refresh-token-rotation