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

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

If the JWT is stored in a cookie, I think it is the same as cookie-based authentication except that the server does not need to have sessions to verify the cookie/token. There is still a risk of CSRF if no special measure is implemented. Isn't JWT stored in a cookie? If the JWT is stored in localStorage/sessionStorage, then there is no cookie involved so don't need to protect against CSRF. The question is how to send the JWT to the server. I found here that it is suggested to use jQuery to send the JWT by HTTP header of ajax requests. So, only the ajax requests can do the authentication? Also, I found one more blog that points to use "Authorization header" and "Bearer" to send the JWT. I don't understand the method the blog talks about. Could someone please explain more about "Authorization header" and "Bearer"? Does this make the JWT transmitted by HTTP header of ALL requests? If yes, what about CSRF?


JWT令牌很流行,因为在OAuth 2.0和OpenID Connect等新的授权和身份验证协议中,它们被用作默认的令牌格式。

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

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

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

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

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

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

双提交Cookies方法

Store JWT in a HttpOnly cookie and used it in secure mode to transfer over HTTPS. Most of CSRF attacks have a different origin or referrer header with your original host in their requests. So check if you have any of them in the header, are they coming from your domain or not! If not reject them. If both origin and referrer are not available in the request then no worries. You can rely on the result of X-XSRF-TOKEN header validation results which I explain in the next step. While the browser will automatically supply your cookies for the domain of the request, there is one useful limitation: the JavaScript code that is running on a website cannot read the cookies of other websites. We can leverage this to create our CSRF solution. To prevent CSRF attacks, we must create an extra Javascript readable cookie which is called: XSRF-TOKEN. This cookie must be created when the user is logged in and should contain a random, un-guessable string. We also save this number in the JWT itself as a private claim. Every time the JavaScript application wants to make a request, it will need to read this token and send it along in a custom HTTP header. Because these operations (reading the cookie, setting the header) can only be done on the same domain of the JavaScript application, we can know that this is being done by a real user who is using our JavaScript application.

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也可能被XST方法这样的高级XSS攻击捕获。

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

点击此处阅读更多信息:

Angular的XSRF:如何工作 在哪里存储您的jwt - cookie vs HTML5 Web存储

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

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

检查动机

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

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

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

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

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

为什么这对CSRF是安全的?

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

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

https://dev.to/cotter/localstorage-vs-cookies-all-you-need-to-know-about-storing-jwt-tokens-securely-in-the-front-end-15id

在web浏览器中,您可以将JWT存储在本地/会话存储或cookie中。两者都有弱点。您可以选择您喜欢的,但是您应该将安全性作为一个整体来保护,并且应该对过程进行良好的设计。如果您只防止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

Both storage and cookies are vulnerable to XSS in some meaning. With javascript code you can read storage and you can send requests to server with cookies included by browser as you are on the same domain. If you use user inputs, you should escape/sanitize them. You can also use header x-xss-protection. The most problematic is malicious code in 3rd party js libs as you cannot escape it and it runs on the same domain. You can prevent user data to be stolen by your mistake, but such code can cause different problems to your application and users.

JWT应该存储在cookie中。你可以根据需要使用httponly和secure标志。

为了防止CSRF,如果适合你的应用程序,同样的站点cookie属性可以设置为严格的-它将阻止你的站点的登录用户从任何其他站点跟踪到你的站点的任何链接。

恕我冒昧,CSRF保护应该与使用双重提交Cookie技术的身份验证分开进行:

在服务器上,生成一个随机字符串(csrf_token)并将其设置在cookie中。 在客户机上,获取cookie值并将其作为头值提交。 在服务器上,比较来自cookie和报头的值。它们应该匹配。