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

zer*_*ing 41 javascript security cookies web-services jwt

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

哪个是最好的选择?

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

那么它将如何保护CSRF?

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

curl -v -X POST -H "Authorization: Basic VE01enNFem9FZG9NRERjVEJjbXRBcWJGdTBFYTpYUU9URExINlBBOHJvUHJfSktrTHhUSTNseGNh"
Run Code Online (Sandbox Code Playgroud)

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

http://exmple.com?jwt=token
Run Code Online (Sandbox Code Playgroud)

如果我通过AJAX发出请求,那么我可以设置一个标题jwt: [token],然后我可以从标题中读取标记.

更新2

我安装了高级REST客户端谷歌浏览器扩展,并能够将令牌作为自定义标头传递.在向服务器发出GET请求时,是否可以通过Javascript设置此标头数据?

Joã*_*elo 87

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

选项1 - 网络存储(localStoragesessionStorage)

优点

  • 浏览器不会自动包括网络存储任何东西到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
Run Code Online (Sandbox Code Playgroud)

选项2 - 仅HTTP的cookie

优点

  • 容易受到XSS 的攻击
  • 浏览器会自动在满足cookie规范的任何请求中包含令牌(域,路径和生命周期)
  • cookie可以在顶级域中创建,并在子域执行的请求中使用

缺点

  • ⚠️它容易受到CSRF的影响
  • 您需要注意并始终考虑子域中cookie的可能用法
  • Cherry挑选应该包含cookie的请求是可行但更麻烦
  • 您可能(仍然)遇到一些问题,浏览器处理cookie的方式差异很小
  • ⚠️如果您不小心,可以实施易受XSS攻击的CSRF缓解策略
  • 服务器端需要验证cookie以进行身份​​验证,而不是更合适的Authorization标头

用法

您无需在客户端执行任何操作,因为浏览器会自动为您处理任何事情.

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

优点

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

缺点

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

用法

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

document.cookie = "token=asY-x34SfYPk"; // write
console.log(document.cookie); // read
Run Code Online (Sandbox Code Playgroud)

补充说明

这似乎是一个奇怪的选择,但它确实有一个很好的好处,你可以让存储可用于顶级域和所有子域,这是Web存储不会给你的东西.但是,实施起来更复杂.


结论 - 最终说明

对大多数常见情况的建议是选择1,主要是因为:

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

另请注意,基于cookie的选项也大不相同,因为选项3 cookie仅用作存储机制,因此它几乎就像是客户端的实现细节.但是,备选方案2意味着更传统的处理身份验证的方式; 为了进一步阅读这个cookie vs令牌,你可能会发现这篇文章很有趣:Cookies vs Tokens:The Definitive Guide.

最后,没有一个选项提到它,但当然必须使用HTTPS,这意味着应该适当地创建cookie以考虑到这一点.

  • 从我所看到的,通过引入 `Set-SameSite`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite) 属性, Cookie` 标头选项 2 [getting](https://caniuse.com/?search=samesite) 不受 CSRF 攻击。特别是在将“Lax”设置为默认值之后。换句话说,选项 2 可能更安全,即使不是现在,至少在不久的将来也是如此。目前还不清楚您所说的“如果您不小心,您可能会实施容易受到 XSS 攻击的 CSRF 缓解策略”到底是什么意思。 (3认同)
  • 精湛的解释!我也觉得选项1至少可以满足我对内部网站点的要求,但公共网络应用程序可能很棘手. (2认同)

Flo*_*lli 22

请访问以下网站:https://auth0.com/blog/2014/01/07/angularjs-authentication-with-cookies-vs-token/

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

curl -v -X POST -H "Authorization: Bearer YOUR_JWT_HERE"
Run Code Online (Sandbox Code Playgroud)

使用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>
Run Code Online (Sandbox Code Playgroud)


小智 11

此博客文章对浏览器存储与Cookie进行了良好的并排比较,并解决了每种情况下的每次潜在攻击.https://stormpath.com/blog/where-to-store-your-jwts-cookies-vs-html5-web-storage/

较短的答案/扰流板:cookie并在jwt中添加xsrf令牌.博客文章中的详细说明.

  • 您链接的Disqus评论讨论似乎消失了。您能否详细说明一下StormPath方法的缺点以及如何克服它们? (2认同)

T. *_*yya 6

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

如果您想在长时间会话期间保留 JWT(例如,令牌仅过期 15 分钟,则为 1 小时),请在令牌即将过期时在后台以静默方式再次登录用户。

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

顺便说一句,刷新令牌是最不有害的。作为补充,您应该确保遵循缓解 XSS 的最佳实践。

localStorage、sessionStorage 和 cookie 都有其漏洞。

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


fad*_*ire 5

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

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

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

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

这将同时避免 XSS 和 CSRF