在基于浏览器的应用程序中保存 JWT 的位置以及如何使用它

IT技术 javascript web-services security cookies jwt
2021-02-07 12:22:07

我正在尝试在我的身份验证系统中实施 JWT,但我有几个问题。为了存储令牌,我可以使用 cookie,但也可以使用localStoragesessionStorage

哪个是最好的选择?

我已经读过 JWT 保护网站免受 CSRF 的影响。但是,我无法想象假设我将 JWT 令牌保存在 cookie 存储中会如何工作。

那么它将如何保护免受 CSRF 的影响?

更新 1
我看到了一些使用示例,如下所示:

curl -v -X POST -H "Authorization: Basic VE01enNFem9FZG9NRERjVEJjbXRBcWJGdTBFYTpYUU9URExINlBBOHJvUHJfSktrTHhUSTNseGNh"

当我从浏览器向服务器发出请求时,我该如何实现?我还看到有些人在 URL 中实现了令牌:

http://exmple.com?jwt=token

如果我通过 AJAX 发出请求,那么我可以设置一个标头jwt: [token],然后我可以从标头读取令牌。

更新 2

我安装了 Advanced REST Client Google Chrome 扩展程序,并且能够将令牌作为自定义标头传递。向服务器发出 GET 请求时,是否可以通过 Javascript 设置此标头数据?

5个回答

选择存储更多的是权衡,而不是试图找到明确的最佳选择。让我们来看看几个选项:

选项 1 - 网络存储(localStoragesessionStorage

优点

  • 浏览器不会自动将 Web 存储中的任何内容包含到 HTTP 请求中,使其不易受到 CSRF 的攻击
  • 只能由在创建数据的完全相同域中运行的 Javascript 访问
  • 允许使用语义最正确的方法在 HTTP(Authorization带有Bearer方案标头)中传递令牌身份验证凭据
  • 挑选应该包含身份验证的请求非常容易

缺点

  • 无法被在创建数据的子域中运行的 Javascript 访问(写入的值example.com不能被读取sub.example.com
  • ⚠️ 易受 XSS 攻击
  • 为了执行经过身份验证的请求,您只能使用允许您自定义请求的浏览器/库 API(在Authorization标头中传递令牌

用法

在执行请求时,您可以利用浏览器localStoragesessionStorageAPI 来存储和检索令牌。

localStorage.setItem('token', 'asY-x34SfYPk'); // write
console.log(localStorage.getItem('token')); // read

选项 2 - 仅 HTTP cookie

优点

  • 这是容易受到XSS
  • 浏览器会自动在任何符合 cookie 规范(域、路径和生存期)的请求中包含令牌
  • cookie 可以在顶级域中创建并用于子域执行的请求中

缺点

  • ⚠️ 易受 CSRF 攻击
  • 您需要注意并始终考虑子域中 cookie 的可能用途
  • Cherry 选择应该包含 cookie 的请求是可行的,但更麻烦
  • 您可能(仍然)遇到一些问题,浏览器处理 cookie 的方式略有不同
  • ⚠️ 如果你不小心你可能会实施一个容易受到 XSS 攻击的 CSRF 缓解策略
  • 服务器端需要验证 cookie 进行身份验证,而不是更合适的Authorizationheader

用法

您不需要在客户端做任何事情,因为浏览器会自动为您处理事情。

选项 3 -服务器端忽略Javascript 可访问 cookie

优点

  • 容易受到 CSRF 的影响(因为它被服务器忽略了
  • cookie 可以在顶级域中创建并用于子域执行的请求中
  • 允许使用语义最正确的方法在 HTTP(Authorization带有Bearer方案标头)中传递令牌身份验证凭据
  • 挑选应该包含身份验证的请求有点容易

缺点

  • ⚠️ 易受 XSS 攻击
  • 如果您不小心设置 cookie 的路径,那么浏览器会自动将 cookie 包含在请求中,这将增加不必要的开销
  • 为了执行经过身份验证的请求,您只能使用允许您自定义请求的浏览器/库 API(在Authorization标头中传递令牌

用法

document.cookie在执行请求时,您可以利用浏览器API 来存储和检索令牌。此 API 不像 Web 存储(您获得所有 cookie)那样细粒度,因此您需要额外的工作来解析您需要的信息。

document.cookie = "token=asY-x34SfYPk"; // write
console.log(document.cookie); // read

补充笔记

这似乎是一个奇怪的选择,但它确实有一个很好的好处,即您可以将存储用于顶级域和所有子域,这是 Web 存储不会给您的。然而,它的实现更加复杂。


结论 - 最后的笔记

对最常见场景的建议是使用 Option 1,主要是因为:

  • 如果您创建一个 Web 应用程序,您需要处理 XSS;始终,独立于您存储令牌的位置
  • 如果您不使用基于 cookie 的身份验证 CSRF 甚至不应该出现在您的雷达上,因此无需担心

另请注意,基于 cookie 的选项也有很大不同,因为选项 3 cookie 纯粹用作存储机制,因此它几乎就像是客户端的实现细节。然而,选项 2 意味着更传统的处理身份验证的方式;要进一步阅读 cookie 与令牌的相关内容,您可能会发现这篇文章很有趣:Cookie 与令牌:权威指南

最后,没有一个选项提到它,但使用 HTTPS 当然是强制性的,这意味着应该适当地创建 cookie 以考虑到这一点。

来这里是为了选项 2!在我看来是最好的,虽然在语义上不是最正确的
2021-03-19 12:22:07
精彩的解释!我也觉得选项 1 至少对于我对 Intranet 站点的要求是最好的,但公共 Web 应用程序可能很棘手。
2021-03-28 12:22:07
从我可以在引入的看到SameSite的属性Set-Cookie头选项2无懈可击的CSRF。尤其是在Lax设置默认值之后。换句话说,选项 2 可能更安全,如果不是现在,那么至少在不久的将来。并且不清楚您所说的“如果您不小心,您可能会实施易受 XSS 攻击的 CSRF 缓解策略”的确切含义。
2021-03-31 12:22:07

[编辑] 这个答案是公认的,但是 João Angelo 的回复更详细,应该考虑。有一点需要注意,并且由于自 2016 年 11 月以来安全实践发生了变化,因此应实施选项 2 以支持选项 1。

看这个网站:https : //auth0.com/blog/2014/01/07/angularjs-authentication-with-cookies-vs-token/

如果您想存储它们,您应该使用 localStorage 或 sessionStorage(如果可用)或 cookie。您还应该使用 Authorization 标头,但不要使用 Basic 方案,而是使用 Bearer 一:

curl -v -X POST -H "Authorization: Bearer YOUR_JWT_HERE"

使用 JS,您可以使用以下代码:

<script type='text/javascript'>
// define vars
var url = 'https://...';

// ajax call
$.ajax({
    url: url,
    dataType : 'jsonp',
    beforeSend : function(xhr) {
      // set header if JWT is set
      if ($window.sessionStorage.token) {
          xhr.setRequestHeader("Authorization", "Bearer " +  $window.sessionStorage.token);
      }

    },
    error : function() {
      // error handler
    },
    success: function(data) {
        // success handler
    }
});
</script>
我用一些(未测试)代码行更新了我的答案。您只需在每个请求中设置授权标头,并让服务器验证 JWT 是否有效
2021-03-15 12:22:07
移动浏览器支持 Cookie。与电脑上的浏览器一样,由于浏览器本身或浏览器的配置,可能会有一些限制(例如,可能会拒绝 3rd 方 cookie)。请参阅stackoverflow.com/questions/4489733/cookies-on-mobile-phone
2021-03-16 12:22:07
我的问题是,没有ajax怎么办,好像不太可能吧?
2021-03-23 12:22:07
“建议不要存储 JWT”是什么意思。您如何在下一个请求中发送 JWT 令牌?您必须将令牌存储在 localStorage、sessionStorage 或 cookie 中。
2021-03-26 12:22:07
我想通过 jwt 替换 cookie 会话存储。现在,当我向服务器发出 GET 请求时,我该如何提供此令牌?是的,我会将令牌保存在会话或本地存储中,但困难在于,如何将每个请求(也 GET)的令牌发送到服务器。我知道当我使用ajax请求时我可以请求header,但是当我不这样做时,我如何将令牌发送到服务器?
2021-04-10 12:22:07

这篇博文对浏览器存储与 cookie 进行了很好的并排比较,并解决了每种情况下的所有潜在攻击。https://stormpath.com/blog/where-to-store-your-jwts-cookies-vs-html5-web-storage/

简短的答案/剧透:cookies 并在 jwt.js 中添加 xsrf 令牌。博文中有详细解释。

您链接的 Disqus 讨论似乎已消失。您能否详细说明一下 StormPath 方法的缺点以及如何克服它们?
2021-03-25 12:22:07
该特定解决方案仍然存在应注意的漏洞。发布的文章会缓和针对该文章的评论。这是更深入的讨论(没有政治公司议程) - disqus.com/home/discussion/stormpath/...
2021-03-27 12:22:07

截至2021和事物发展有点引进的的SameSite:国际机场/严格的选项饼干上最nowdays浏览器

因此,要详细说明 João Angelo 的回答,我想说现在最安全的方法是:

使用以下选项JWT存储在 cookie 中

  • 仅Http
  • 安全的
  • SameSite:松散或严格

这将同时避免 XSS 和 CSRF

你应该永远永远不要在内存之外存储 JWT。

如果您想在长时间会话期间保留 JWT(比如 1 小时,当令牌只有 15 分钟到期时),请在令牌即将到期时再次在后台静默记录用户。

如果要跨会话保留 JWT,则应使用刷新令牌顺便说一句,其中大部分时间也用于上述目的。您应该将其存储在 HttpOnly cookie 中(好吧,更准确地说,服务器集是通过 Set-Cookie 标头,前端调用 /refresh_token API 端点。)

顺便说一句,刷新令牌是最不坏的;为了补充它,您应该确保遵循最佳实践来缓解 XSS。

localStorage、sessionStorage 和 cookie 都有其漏洞。

这是我读过的关于 JWT 的最佳指南:https ://blog.hasura.io/best-practices-of-using-jwt-with-graphql/

该指南是金
2021-03-15 12:22:07
- 作为 id_token 的 JWT 就像您的用户凭据 - 作为 access_token 的 JWT 就像您的会话令牌 最安全的选项是在内存中。结帐这个深入了解
2021-04-01 12:22:07