为啥不将 JWT 访问令牌存储在内存中并在 cookie 中刷新令牌?

Posted

技术标签:

【中文标题】为啥不将 JWT 访问令牌存储在内存中并在 cookie 中刷新令牌?【英文标题】:Why not store JWT access token in memory and refresh token in cookie?为什么不将 JWT 访问令牌存储在内存中并在 cookie 中刷新令牌? 【发布时间】:2022-01-15 02:25:43 【问题描述】:

在处理基于浏览器的应用程序时,关于安全存储 JWT 令牌的主题已经提出了很多问题。共识似乎是应该使用http-only,secure cookies。但是,当涉及短期访问令牌和长期刷新令牌时,存储 JWT 令牌似乎存在许多变化。

我发现了以下变体:

1.将 JWT 访问令牌和刷新令牌存储在仅限 http 的安全 cookie 中

优点:

无法从 javascript 访问访问令牌和刷新令牌

缺点:

引入了 CSRF 漏洞,因此还必须添加 CSRF 令牌

这里的最佳答案建议添加 CSRF 令牌:https://***.com/a/37396572/6735966

2。将 JWT 访问令牌存储在内存中,并在 http-only 安全 cookie 中刷新令牌

优点:

无法从 Javascript 访问刷新令牌 通过 Javascript 发送访问令牌,因此访问令牌不会受到 CSRF 的攻击 刷新 cookie 只能用于获取新的访问令牌。使用正确的 CORS 设置,恶意方无法通过跨站点请求从响应中读取访问令牌。因此,这种方法似乎不受 CSRF 影响。

缺点:

可以通过Javascript访问访问令牌(但是访问令牌很快就会过期)

在这里推荐,但获得的票数比顶部帖子少得多:https://***.com/a/63593954/6735966

3.将刷新令牌存储在内存中,将 JWT 访问令牌存储在仅 http 的安全 cookie 中

优点:

无法从 Javascript 访问访问令牌 通过 Javascript 发送刷新令牌,因此刷新令牌不会受到 CSRF 的攻击

缺点:

可以从 Javascript 访问寿命更长的刷新令牌 访问令牌易受 CSRF 攻击

这里的最佳答案中描述了类似的方法:https://***.com/a/54378384/6735966

考虑到在内存中存储 JWT 访问令牌和在 http-only 中刷新令牌的利弊,安全 cookie 对我来说绝对是个好主意。然而,尽管关于这个话题有很多问题,但投票最多的答案中没有一个甚至考虑过这种方法。因此我的问题是:为什么不将 JWT 访问令牌存储在内存中并在 cookie 中刷新令牌,而是使用其他方法之一?

【问题讨论】:

【参考方案1】:

将 cookie 存储在内存中是可行的,但一个问题是,如果您有同一个客户端的多个实例。

另一种方法是像在 ASP.NET 核心中那样进行操作,默认情况下,cookie 以加密方式存储在会话 cookie 中。但是,此 cookie 不能用于访问任何 API,因为它无法从 JavaScript 访问。此外,为避免 CSRF 问题,您通常会添加通用防伪令牌/cookie 以防止 CSRF 攻击。

所以,将令牌存储在 ASP.NET Core 中的大多数方法是在会话 cookie 中。但与此同时,这会使 cookie 增长不少,所以当你觉得 cookie 变大时,你就开始考虑将它们存储在内存中或数据库/Redis 存储中。

例如,在 .NET 中,已经通过使用 SessionStore 内置了对此的支持。下图试图展示它是如何工作的,其中 SessionKey 存储在 cookie 中,然后令牌存储在内存/后端中。

是的,这就是它在 ASP.NET Core 中的工作方式,我相信您也可以在其他平台上找到类似的方法。

另一种方法是使用似乎越来越流行的BFF pattern。

【讨论】:

您介意解释一下为什么拥有相同客户端的多个实例是个问题吗? 如果您有多个实例,那么如何在不同服务之间共享令牌? (除非您在负载均衡器中使用粘性会话)。通过服务,您更愿意让它们无国籍。还有一个问题是确保加密的 cookie 在不同的服务实例中工作,对于一些需要额外注意和配置的框架。 对不起,我还是不明白。我假设对于多个客户端实例,您的意思是例如两个对同一个 REST api 进行身份验证的单页应用程序 (SPA)。 JWT 访问令牌存储在浏览器内存中,刷新令牌存储在 cookie 中。既然 REST api 是无状态的,为什么服务需要共享令牌? 如果您使用的是经典后端站点还是 SPA,您的问题有点不清楚,我假设是前者。 SPA 应用程序的问题是安全性以及如何管理令牌。一种方法是使用 BFF 模式来避免处理在浏览器中存储和保护令牌的复杂性。 我认为后端处理的越多,安全性就越好。

以上是关于为啥不将 JWT 访问令牌存储在内存中并在 cookie 中刷新令牌?的主要内容,如果未能解决你的问题,请参考以下文章

为啥我应该使用 JWT 而不是简单的散列令牌

存储 JWT 令牌

为啥使用 JWT 刷新令牌

如何在 vue 中存储、管理 REST API JWT 身份验证令牌?

在永久存储中跟踪 JWT

jwt访问令牌存储在spring boot中的哪里?