安全的单站点 JWT 实现

A secure single site JWT implementation

我阅读了很多关于 JWT 的文章,发现很难以安全的 CSRF 和 XSS 证明方式使用它们。
直到我意识到我的代币可能不会离开我的独特领域...... 这让我想到了实施的想法:

  1. 使用刷新令牌和访问令牌。这样我就可以为访问令牌设置较短的过期时间并限制数据库调用以验证用户,并且仅当访问令牌过期时通过在向用户。
  2. 我知道如果我将令牌存储在 local/session 存储中,它将容易受到 XSS 攻击,但如果我将它存储在仅 HTTP 的 cookie 中,它将容易受到 CSRF 攻击。
  3. 因为我不需要访问 JavaScript 中的令牌,而且我只需要在一个网站中使用这个令牌,所以我认为我可以将访问令牌存储在一个仅限 HTTP 的 cookie 中(即它受到 XSS 保护的方式)以及安全标志和相同的站点标志设置为严格(防止 CSRF)。
  4. 关于刷新令牌,这次我可以将其存储在具有相同安全标志但没有相同站点标志的仅限 HTTP 的 cookie 中。由于服务器永远不会只根据刷新令牌执行任何操作,因此我认为它不会受到 CSRF 攻击。服务器对刷新令牌所做的唯一事情就是发回一个新的访问令牌,如果我理解得很好的话,它无法从 CSRF 攻击者那里读取。 CSRF 漏洞允许攻击者向服务器发出请求(服务器将自动包含 HTTP-only cookie),但他无法读取此请求的响应。

我不知道这个 JWT 的实现是否安全,或者我是否遗漏了什么...

这就是我要问你的问题(JWT 和网络安全专家)这是一个好的 JWT 实现吗?

首先,谈谈 JWT。在大多数情况下,老式普通服务器端会话(具有长的随机会话 ID,正确存储在安全的 httpOnly,也可能是 SameSite cookie 中)比任何 JWT“更安全”。原因是 JWT 将状态存储在客户端上,攻击者更容易获得它,可以针对所涉及的密码执行离线攻击,而且 JWT 是纯文本并且只保护它们的完整性(即它们受到保护以防止客户端-侧更改,但不反对默认查看内容,尽管您也可以使用 JWE 和加密的 JWT)。最后但并非最不重要的一点是,实现更复杂,简单性对安全性非常重要。

因此,当您需要 JWT 提供的好处时,您会使用它。

一个论点是它是无状态的。不过,这往往被高估了。首先,在大多数应用程序中,瓶颈不是在数据库中查找会话数据所需的数据库查找。在一些非常引人注目、高流量的应用程序中,它实际上可能是,但你可能不会每天都开发这样的东西。通常做一个数据库查找就可以了,它使整个事情变得简单多了。其次,有时您需要撤销令牌,即。您希望能够强制注销用户。即使您使用 JWT 实现无状态,您也必须在数据库中存储 某些内容 ,例如已撤销令牌的列表,并且检查它也将是数据库往返。撤销 JWT 根本无法以纯无状态的方式实现。

另一个好处是您可以将它用于多个来源。如果你得到一个 httpOnly 会话 cookie,它将由浏览器管理,javascript 不能将它发送到其他来源,比如 API 或其他东西 - 这就是重点,JS 无法访问。但有时你确实需要它,特别是在单点登录场景中,你有一个身份提供者(一个提供登录的组件),以及你想要在其上使用访问令牌的服务(例如 APIs) .在这种情况下,经典 cookie 将不起作用,而 JWT 非常方便。

简而言之,当您确实需要无状态或将令牌发送到多个来源的选项时,您可以使用 JWT。根据经验,如果您可以将 JWT 存储在 httpOnly cookie 中,您很可能根本不需要它,并且可以只使用普通的旧服务器端会话。

然后假设您决定使用 JWT,那么刷新令牌呢?它们是基于令牌的身份验证的另一个通常被误解的功能。使用刷新令牌的要点是能够使用短期访问令牌,因此如果访问令牌被泄露,它至少是有时间限制的。但是,如果您以与访问令牌相同的方式存储刷新令牌,为什么攻击者也无法获取它?

只有刷新令牌才有意义,如果你以不同的方式存储它,否则你可能只有长期访问令牌,而且安全性不会降低。

一个典型的事情是拥有一个身份提供者(“登录服务器”或 IdP),它在身份提供者来源的 httpOnly cookie 中设置一个刷新令牌(或只是一个普通的旧会话 ID),所以每当客户端向 IdP 发出请求,如果他们已经登录,它就“正常工作”,并且可以在没有用户交互的情况下提供新的访问令牌。然后将访问令牌存储在类似 localStorage(用于服务源)的东西中,容易受到 XSS 攻击,但至少有时间限制。如果您没有这种分离,单独的刷新令牌可能没有多大意义。

最后一点,所有这些应该很少由你自己实现。您使用的任何语言或框架可能已经有一个或多个已知的良好的、维护的身份验证实现。你应该只使用那些,但当然它仍然非常值得理解。

请注意,在任何实际应用程序中,可能会有一些微妙之处和某些情况会在某种程度上改变您想要做的事情,但以上是考虑安全身份验证的一般方法。