在浏览器的哪里存储JWT ?如何防范CSRF?

我知道基于cookie的身份验证。SSL和HttpOnly标志可以应用于保护基于cookie的身份验证不受MITM和XSS的影响。然而,为了保护它不受CSRF的影响,还需要采取更多的特殊措施。它们只是有点复杂。(参考)

最近,我发现JSON Web Token (JWT)作为一种身份验证解决方案非常热门。我了解编码、解码和验证JWT的知识。然而,我不明白为什么有些网站/教程告诉我们,如果使用JWT,就不需要CSRF保护。我已经阅读了很多,并试图总结以下问题。我只是希望有人能提供一个关于JWT的更大的图景,并澄清我对JWT的误解。

  1. 如果JWT存储在cookie中,我认为它与基于cookie的身份验证是一样的,只是服务器不需要会话来验证cookie/令牌。如果不采取特别措施,仍有发生CSRF的风险。JWT不是存储在cookie中吗?

  2. 如果JWT存储在localStorage/sessionStorage中,则不涉及cookie,因此不需要保护CSRF。问题是如何将JWT发送到服务器。我发现在这里,建议使用jQuery通过ajax请求的HTTP头发送JWT。那么,只有ajax请求才能进行身份验证吗?

  3. 此外,我还发现了一个博客,指向使用“授权头文件”;和“;Bearer"发送JWT。我不明白博客上说的方法。有人能解释一下“授权标题”吗?和“Bearer" ?这使得JWT传输的HTTP头的所有请求?如果有,CSRF怎么办?

143839 次浏览

JWT令牌很受欢迎,因为它们在新的授权和身份验证协议(如OAuth 2.0OpenID连接)中用作默认令牌格式。

当令牌存储在cookie中时,浏览器会自动将它与每个请求一起发送到同一个域,这仍然容易受到CSRF攻击。

承载身份验证是HTTP中定义的身份验证方案之一。它基本上意味着在请求的授权HTTP报头中插入(JWT)令牌。浏览器将自动为你做这个,所以它不适合保护你的网站。由于浏览器不会自动向您的请求添加标头,因此它不容易受到CSRF攻击,这取决于您的身份验证信息自动提交到原始域。

承载方案通常用于保护通过AJAX调用或移动客户端使用的web api (REST服务)。

我们需要将JWT存储在客户端计算机上。如果我们将它存储在LocalStorage/SessionStorage中,那么它很容易被XSS攻击捕获。如果我们将其存储在cookie中,那么黑客就可以在CSRF攻击中使用它(不读取它),并模拟用户,联系我们的API,并代表用户发送操作或获取信息的请求。

但是有几种方法可以保护cookie中的JWT不容易被窃取(但仍然有一些先进的技术可以窃取它们)。但是如果你想依赖LocalStorage/SessionStorage,那么它可以通过一个简单的XSS攻击来访问。

因此,为了解决CSRF问题,我在应用程序中使用了双重提交cookie。

双提交Cookies方法

  1. 将JWT存储在HttpOnly cookie中,并在安全模式下使用它通过HTTPS传输。

  2. 大多数CSRF攻击在其请求中与您的原始主机有不同的起源或引用头。所以检查你是否有任何他们在头,他们是来自你的域或不是!如果不拒绝他们。如果在请求中没有origin和referrer,那么不用担心。您可以依赖X-XSRF-TOKEN报头验证结果,我将在下一步中解释这个结果。

  3. 虽然浏览器会自动为您的请求域提供cookie,但有一个有用的限制:网站上运行的JavaScript代码不能读取其他网站的cookie。我们可以利用这一点来创建我们的CSRF解决方案。为了防止CSRF攻击,我们必须创建一个额外的Javascript可读cookie,称为XSRF-TOKEN。这个cookie必须在用户登录时创建,并且应该包含一个随机的、不可猜测的字符串。我们还将这个数字保存在JWT中作为私有声明。每次JavaScript应用程序想要发出请求时,它都需要读取这个令牌并在自定义HTTP报头中发送它。因为这些操作(读取cookie,设置报头)只能在JavaScript应用程序的同一域中完成,所以我们可以知道这是由正在使用我们的JavaScript应用程序的真实用户完成的。

Angular JS让你的生活变得简单

幸运的是,我在我们的平台上使用的是Angular JS, Angular封装了CSRF令牌方法,这让我们更容易实现它。对于Angular应用程序向服务器发出的每个请求,Angular $http服务都会自动执行以下操作:

  • 在当前域中查找名为XSRF-TOKEN的cookie。
  • 如果找到该cookie,它将读取该值并将其作为X-XSRF-TOKEN报头添加到请求中。

因此,客户端实现将自动为您处理!我们只需要在服务器端当前域上设置一个名为XSRF-TOKEN的cookie,当我们的API从客户端获得任何调用时,它必须检查X-XSRF-TOKEN报头,并将其与JWT中的XSRF-TOKEN进行比较。如果匹配,则用户是真实的。否则,这是一个伪造的请求,你可以忽略它。这种方法的灵感来自于“Double Submit cookie”。方法。

谨慎

在现实中,你仍然容易受到XSS的影响,只是攻击者不能窃取你的JWT令牌供以后使用,但他仍然可以使用XSS代表你的用户发出请求。

无论你是将JWT存储在localStorage中,还是将XSRF-token存储在非HttpOnly cookie中,都可以被XSS轻松获取。甚至你在HttpOnly cookie中的JWT也可以被高级XSS攻击捕获,比如XST method

因此,除了Double Submit Cookies方法之外,还必须始终遵循针对XSS的最佳实践,包括转义内容。这意味着删除任何可能导致浏览器执行您不希望它执行的操作的可执行代码。通常,这意味着删除// <![CDATA[标记和HTML属性,导致JavaScript被计算。

点击此处阅读更多信息:

关于存储jwt的另一个角度:

  1. jwt永远不应该存储在你的localStorage中
  2. 事实上,它们甚至不应该存储在你的cookie中除非你能够实施非常严格的CSRF保护

检查动机

  • JWT作为id_token就像您的用户凭证
  • JWT作为access_token就像您的会话令牌

最安全的选项是内存中检查这个深度潜水

现在在2020年,只需将JWT令牌存储在带有SameSite=strict的cookie中就可以击败CSRF。当然,也要保留securehttpOnly

https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite

将访问令牌存储在内存中,并将刷新令牌存储在cookie中

为什么这对CSRF是安全的?

虽然提交给/refresh_token的表单可以工作,并且将返回一个新的访问令牌,但如果攻击者使用的是HTML表单,则无法读取响应。为了防止攻击者成功地发出获取或AJAX请求并读取响应,这需要正确设置授权服务器的CORS策略,以防止来自未经授权的网站的请求。

你可以在这里阅读更多信息:

< a href = " https://dev。/销/ localstorage-vs-cookies-all-you-need-to-know-about-storing-jwt-tokens-securely-in-the-front-end-15id noreferrer“rel = > https://dev.to/cotter/localstorage-vs-cookies-all-you-need-to-know-about-storing-jwt-tokens-securely-in-the-front-end-15id < / >

在浏览器中,你可以将JWT存储在本地/会话存储饼干中。两者都有弱点。您可以选择您喜欢的,但是您应该将安全性作为一个整体来保护,并且应该对过程进行良好的设计。如果您只防止XSRF和XSS,这将对您没有帮助。这是对你问题的简短回答。

首先你要防止用户数据被窃取。非常有问题的是XSS攻击。如果您使用存储,攻击者可以窃取令牌-发送令牌到他的服务器,并请求窃取用户数据。如果你使用httpOnly cookie,他不能窃取令牌,但他可以发送请求(浏览器包含cookie,如果脚本在同一个域上),读取响应并将用户数据发送到他的服务器。结果都一样。

为了防止将数据发送到不同域的服务器,可以使用Content-Security-Policy报头。我建议学习所有的安全头和网络安全。好的资源是OWASP。这个论坛不是让你写很多页的。

XSRF (CSRF)

如果您使用cookie,那么应用程序很容易受到这种攻击。

如何预防:

设置httpOnly、secured和SameSite=strict标志。你也可以在没有httpOnly的情况下使用第二个cookie XSRF-TOKEN,并在头部X-Xsrf-Token中发送它的值。但如果浏览器支持,这是通过SameSite标志解决的。

XSS

在某种意义上,存储和cookie都容易受到XSS的攻击。有了javascript代码,你可以读取存储,你可以发送请求到服务器与浏览器包括cookies,因为你是在同一个域。如果您使用用户输入,您应该转义/清除它们。你也可以使用报头x-xss-protection。最大的问题是第三方js库中的恶意代码,因为你无法逃脱它,它运行在同一个域上。您可以防止您的错误窃取用户数据,但是这样的代码会给您的应用程序和用户带来不同的问题。