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

我试图在我的身份验证系统中实现 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 设置这个头数据?

69212 次浏览

这个答案是可以接受的,但是来自 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>

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

简短的回答/剧透: cookies 并在 jwt 中添加 xsrf 标记。

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

选项1-网上存储(localStoragesessionStorage)

优点

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

缺点

  • 不能被创建数据的子域中运行的 Javascript 访问(example.com写的值不能被 sub.example.com读取)
  • Something 容易受到 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 以进行身份验证,而不是验证更合适的 Authorization报头

用法

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

选项3-可访问的 JavaScript Cookie 被服务器端忽略

优点

  • 它的 没有易受 CSRF (因为它被服务器忽略了)的攻击
  • Cookie 可以在一个顶级域上创建,并用于子域执行的请求
  • 允许使用语义上最正确的方法在 HTTP 中传递令牌身份验证凭据(具有 Bearer方案的 Authorization头)
  • 挑选应该包含身份验证的请求有些容易

缺点

  • 它很容易受到 XSS 的攻击
  • 如果你不注意设置 cookie 的路径,那么 cookie 会被浏览器自动包含在请求中,这会增加不必要的开销
  • 为了执行经过身份验证的请求,您只能使用允许您定制请求的浏览器/库 API (在 Authorization头中传递令牌)

用法

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

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

附加说明

这可能看起来是一个奇怪的选择,但它确实有一个好处,那就是你可以拥有一个顶级域和所有子域的存储空间,而这些是网络存储不能提供的。然而,实现起来更加复杂。


结语-最后注释

我的 对于大多数常见的情况,建议采用备选方案1,主要是因为:

  • 如果创建 Web 应用程序,则需要处理 XSS; 总是独立于存储令牌的位置
  • 如果不使用基于 cookie 的认证,CSRF 甚至不应该出现在您的雷达上,这样就少了一件需要担心的事情

还要注意的是,基于 cookie 的选项也有很大的不同,因为 Option3 cookie 纯粹用作存储机制,所以它几乎就像是客户端的一个实现细节。然而,选项2意味着处理身份验证的一种更传统的方法; 对于进一步阅读 Cookie 与令牌之间的内容,您可能会发现本文很有趣: Cookies VS Token: 通用指南

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

您应该在内存之外存储一个 JWT。

如果希望在一个长会话期间持久化 JWT (比如令牌到期时间只有15分钟,持续时间为1小时) ,那么每当令牌即将到期时,就悄悄地将用户再次记录到后台。

如果希望跨会话持久化 JWT,应该使用 刷新令牌。顺便说一句,大部分时间也是用于上述目的的。您应该将它存储在一个 HttpOnly cookie 中(更准确地说,服务器集是通过 Set-Cookie 头,前端调用/refresh _ token API 端点)

刷新令牌(BTW)是最小的弊端; 为了补充它,您应该确保遵循最佳实践来减轻 XSS。

LocalStorage、 sessionStorage 和 cookies 都有自己的漏洞。

这是我在 JWT 上读过的最好的指南: Https://blog.hasura.io/best-practices-of-using-jwt-with-graphql/

2021年事情发生了变化开始,随着 同一网站: 宽松/严格选项在大多数浏览器上的引入,饼干也有了一些改进

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

使用以下选项将 智威汤逊存储在 cookie 中

  • HttpOnly
  • 安全
  • 同一网站: 宽松还是严格

这将同时避免 XSS 和 CSRF