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

Posted

技术标签:

【中文标题】如何使用 Next.js 和 Rails 后端安全地存储 JWT?【英文标题】:How do I securely store JWTs with Next.js and a Rails backend? 【发布时间】:2020-01-13 19:37:44 【问题描述】:

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

我在 Rails 端使用 jwt-sessions gem,登录时返回 access_tokenrefresh_tokencsrf 令牌。 csrf 只能通过标头发送,并且对于所有非 GET 或 HEAD 请求都是必需的。 accessrefresh 可以通过标头 由 cookie 给出。

我可以实现后端以在 JSON 响应中返回所有令牌,或设置 cookie,或两者兼而有之。

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

    安全 在 s-s-r 期间以及在浏览器中可用

这是我看到的选项

选项 1:服务器设置 httpOnly Cookies

这是jwt-sessions gem 推荐的。将 CSRF 保存在 localStorage 中,而将其他令牌保存在服务器设置的 httpOnly cookie 中。

这似乎是最安全的,但是通过getInitialProps 的s-s-r 根本不起作用,因为它无法通过fetch 发送cookie。我什至不能手动发送它们,因为我在 JS 中看不到它们。

选项 2:非 httpOnly Cookies

这就是official next example 似乎在做的事情(尽管使用了一个可能是无状态的令牌)

让服务器或浏览器设置 cookie没有 httpOnly。

我可以通过 s-s-r 访问它,但这不会打开我的 XSS 吗?

选项 3:使用浏览器存储

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

????

我错过了什么吗?是非httpOnly,就像在官方的例子中,好吗?有更好的方法吗?还是我必须忽略 s-s-r(因此是 Next.js 的杀手级功能之一)?

【问题讨论】:

next.js 服务器端应用程序是否由与jwt-sessions gem 相同的域提供服务?如果没有,jwt-sessions gem 无法设置 next.js 服务器端渲染可以看到的 cookie。 csrf 只能通过 header 发送,并且对于所有非 GET 或 HEAD 请求到 rail 后端 都需要,因此需要将其传递给任何时候服务器端都可能向后端发出非 GET 或 HEAD 请求时,next.js 服务器端? 【参考方案1】:

让我们检查每个令牌以及将在何处使用它。 Cookie 只能呈现给设置它们的服务器,因此任何 cookie 都需要由使用它的服务器设置。

refresh令牌

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

Set-Cookie: refresh-token=...; domain=rails-backend.example.com; secure; HttpOnly

access令牌

访问令牌将有两种使用方式,它将由next.js客户端呈现给rails后端,并由next.js服务器端使用它来调用rails后端到服务器-side 通过getInitialProps 渲染页面。

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

Set-Cookie: access-token=...; domain=rails-backend.example.com; secure; HttpOnly

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


    'access': "..."

Host: next-js-server-side.example.com
X-Rails-Backend-Access-Token: ...

一旦 next.js 服务器端拥有访问令牌,它就可以设置一个 HttpOnly cookie 以使访问令牌可用于未来的请求。

Set-Cookie: rails-backend-access-token=...; domain=next-js-server-side.example.com; secure; HttpOnly

csrf令牌

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

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


    'csrf': "..."

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

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

Host: next-js-server-side.example.com
X-Rails-Backend-CSRF-Token: ...

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

Set-Cookie: rails-backend-csrf-token=...; domain=next-js-server-side.example.com; secure; HttpOnly

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

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

<input type="hidden" id="rails-backend-csrf-token" value="...">

next.js 的跨站请求伪造

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

【讨论】:

以上是关于如何使用 Next.js 和 Rails 后端安全地存储 JWT?的主要内容,如果未能解决你的问题,请参考以下文章

如何正确配置 Next.js 作为前端和 Express 应用作为后端?

使用 Next.js 后端反应本机

如何在 Next.js 中实现身份验证

使用默认 Next.js 开发服务器代理到后端

Rails Guides - Ruby on Rails 安全指南

Next.js 10.2正式发布,React应用的后端渲染