如何使用Next.js和Rails后端安全地存储JWT?

Nat*_*lor 5 javascript authentication ruby-on-rails jwt next.js

我正在尝试在Next.js前端和Rails API后端之间实现身份验证,并且很难理解如何正确安全地执行此操作。

我在rails端使用了jwt-sessions gem,它在登录时会返回access_token,a refresh_token和csrf令牌。在csrf只能通过报头被发送,并且需要对所有非GET或HEAD的请求。的access和refresh可以通过报头中给定的或由饼干。

我可以实现后端,以返回JSON响应中的所有令牌,或设置cookie,或同时使用两者。

问题是,在客户端,我看不到存储这些令牌的真正方法,即:

  1. 安全
  2. 在SSR和浏览器中均可用

这是我看到的选项

选项1:服务器设置的HTTPOnly Cookie

这就是jwt-sessions宝石的建议。将CSRF保留在localStorage中,但将其他令牌保留在服务器设置的httpOnly cookie中。

这似乎是最安全的方法,但是SSR via getInitialProps根本无法使用,因为它无法通过发送Cookie fetch。我什至无法手动发送它们,因为在JS中看不到它们。

选项2:非httpOnly Cookie

这就是官方的下一个示例似乎所做的(尽管只有一个可能是无状态的令牌)

服务器或浏览器都可以设置没有 httpOnly 的cookie 。

我可以通过SSR访问它,但这不使我对XSS开放吗?

选项3:使用浏览器存储

只需将所有令牌放入localStorage和/或即可sessionStorage。这似乎是最糟糕的选择,因为它在SSR中不起作用,据我所知并不安全。

????

我想念什么吗?是否非httpOnly,如官方示例所示,可以吗?有没有更好的方法?还是我必须忽略SSR(因此是Next.js的杀手级功能之一)?

Cir*_*dec 3

让我们检查一下每个令牌及其用途。Cookie 只能呈现给设置它们的服务器,因此任何 Cookie 都需要由使用它的服务器设置。

refresh代币

如果访问令牌过期,刷新令牌将提交给jwt-sessions身份验证服务器以获取新access令牌csrf。由于刷新令牌既来自认证服务器,又在认证服务器上使用,因此可以HttpOnly由认证服务器将其设置为cookie

Set-Cookie: refresh-token=...; domain=rails-backend.example.com; secure; HttpOnly
Run Code Online (Sandbox Code Playgroud)

access代币

访问令牌将以两种方式使用,它将由 next.js 客户端呈现给 Rails 后端,并由 next.js 服务器端使用它来调用 Rails 后端以进行服务器端渲染页面通过getInitialProps.

从客户端向 Rails 后端提供访问令牌很容易。身份jwt-sessions验证服务器与rails后端运行在同一台服务器上,因此身份验证服务器可以设置HttpOnlycookie。

Set-Cookie: access-token=...; domain=rails-backend.example.com; secure; HttpOnly
Run Code Online (Sandbox Code Playgroud)

从 next.js 服务器端呈现它更加困难,要获取它,客户端需要将访问令牌发送到 next.js 服务器端。标题是最初呈现它的适当方式。为了将其发送到 next.js 服务器端,客户端需要包含在来自身份验证服务器的 json 响应中的访问令牌。

{
    'access': "..."
}
Run Code Online (Sandbox Code Playgroud)
Host: next-js-server-side.example.com
X-Rails-Backend-Access-Token: ...
Run Code Online (Sandbox Code Playgroud)

一旦 next.js 服务器端获得访问令牌,它就可以设置一个HttpOnlycookie 以使访问令牌可用于将来的请求。

Set-Cookie: rails-backend-access-token=...; domain=next-js-server-side.example.com; secure; HttpOnly
Run Code Online (Sandbox Code Playgroud)

csrf代币

csrf 令牌用于保护向 Rails 后端发出的请求免受跨站点伪造请求的影响。它需要由任何向 Rails 后端(客户端和服务器端)发出请求的东西来呈现。

客户端将从身份验证服务器获取 csrf 作为 json 负载的一部分,因为这是将其发送给客户端的唯一安全方法。

{
    'csrf': "..."
}
Run Code Online (Sandbox Code Playgroud)

客户端现在可以使用它向 Rails 后端发出请求,但它不会持久存储。

为了将 csrf 令牌获取到 next.js 服务器端,客户端会将其作为标头发送

Host: next-js-server-side.example.com
X-Rails-Backend-CSRF-Token: ...
Run Code Online (Sandbox Code Playgroud)

next.js 服务器端可以转身并设置HttpOnlycookie,以使 csrf 令牌可用于将来的请求。

Set-Cookie: rails-backend-csrf-token=...; domain=next-js-server-side.example.com; secure; HttpOnly
Run Code Online (Sandbox Code Playgroud)

这个cookie对于向rails后端发送跨站伪造请求是没有用的,因为它只呈现给next-js服务器端,而根本不呈现给rails后端。

next.js 服务器端可以将此令牌返回给客户端,只要它不会被发起跨站点伪造攻击的站点劫持即可。

<input type="hidden" id="rails-backend-csrf-token" value="...">
Run Code Online (Sandbox Code Playgroud)

next.js 跨站请求伪造

next.js 服务器端需要提供自己的跨站点伪造保护。它不能依赖HttpOnly上面设置的 cookie,因为它将出现在跨站点请求中。