为什么不将两个 access_tokens 和两个 refresh_tokens 存储在 cookie 中,一个存储在本地存储中,以防止 XSS 和 CSRF

Why not to have two access_tokens and two refresh_tokens stored one in cookie and one in localstorage to protect from XSS and CSRF

背景

场景是我们有一个 REACT SPA 和一个既是资源服务器又是身份验证服务器的 API。我们想要实现基于简单令牌的身份验证。没有专用的 SSO 服务器,因此不需要 OAuth2。

过去两天我阅读了很多关于如何正确执行此操作的文章,但有一件事仍然困扰着我。有很多关于令牌应该存储在 LocalStorage 还是 cookie 中的讨论? First 容易受到 XSS 攻击,因为注入的代码可以从 LocalStorage 窃取您的数据,但可以防止 CSRF,因为来自恶意网站的伪造请求将无法窃取它。后者容易受到 CSRF 的攻击,因为来自恶意网站的伪造请求会使浏览器一起发送 cookie,但可以防止 XSS,因为恶意网站无法访问原始网站的 LocalStorage。

问题:那为什么不两者都用呢?

为什么不body 创建两个用两个不同的密钥和两个不同的 refresh_token 秘密签名的 JWT?一个 JWT 和 refresh_token 然后存储在 LocalStorage 中,另一个存储在 cookie 中。 如果我 ask google 总是一对一。以任何方式同时拥有反模式或坏主意吗?因为实现它并不是那么多工作...

我认为这应该如何工作

以下是我认为这种流程的工作原理:

  1. 用户在登录表单中输入登录名和密码
  2. React 应用程序将带有登录名和密码的登录请求发送到 /api/auth/login。在 return 中得到:
  1. React 应用在每个下一个请求中发送第一个 JWT 作为 Authentication header 的值,第二个 JWT 由浏览器作为 cookie 自动附加。
  2. API 检查两者的签名,如果两者都正确则对请求进行身份验证
  3. 3 - 4 次重复,直到任何令牌过期(我假设它们的过期时间相同,但为了这一步,让我们更通用)
  4. React 应用使用 JWT 和 refresh_token 从 LocalStorage 向 /api/auth/refresh 发送请求,浏览器将第二个 JWT 作为 cookie 附加,另一个 refresh_token 作为 cookie 附加,因为路径满足 cookie 的条件。
  5. API 验证过期 JWT 的签名并检查 refresh_tokens 是否存在于该用户的持久层中并且没有过期。如果一切正常,将生成新的 JWT 和新的 refresh_tokens 并以与第 2 点相同的方式 returned 到 React APP。

注销时刷新令牌从持久层中删除,因此访问令牌不会被刷新。

如果您认为此流程更安全,为什么需要此刷新令牌?

我得出的结论是,添加刷新令牌确实不会增加用户的安全性,缺点是在 logout/revoke 之后 JWT 仍然有效但刷新令牌是revoked/user 注销了,但我从这个门户网站的许多讨论中设法收集了很多其他专业人士:

我们已经实现了这个流程,这里有两个主要结论:

  • 当 API 和前端托管在同一域(例如,api.example.com 和 example.com)时,此流程非常有效并提供更高的安全性
  • httpOnly 当 API 和前端托管在不同的域(例如,frontend.com 和 api.com)时,Safari 浏览器会阻止 cookie