Tom Abbott很好地总结了JWT sessionStorage and localStorage security: Web存储(本地存储/会话存储)可通过同一域上的JavaScript访问。这意味着在您的站点上运行的任何JavaScript都将可以访问Web存储,因此可能容易受到跨站点脚本(XSS)攻击。简而言之,XSS是一种漏洞,攻击者可以在其中注入将在您的页面上运行的JavaScript。基本的XSS攻击试图通过表单输入注入JavaScript,攻击者将<script>alert('You are Hacked');</script>放入表单中,以查看它是否由浏览器运行并可被其他用户查看。
One thing to keep in mind is whether the JWTs are:
First party (ie. simply for accessing your own server commands)
Third party (ie. a JWT for Google, Facebook, Twitter, etc.)
If the JWT is first-party:
Then it doesn't matter that much whether you store the JWT in local storage, or a secured cookie (ie. HttpOnly, SameSite=strict, and secure) [assuming your site is already using HTTPS, which it should].
This is because, assuming an XSS attack succeeds (ie. an attacker was able to insert Javascript code through a JS dependency that is now running on all visitor browsers), it's "game over" anyway; all the commands which were meant to be secured by the "JWT token verifications", can now be executed by the attacker just by having the script they've inserted into the frontend JS call all the needed endpoints. Even though they can't read the JWT token itself (because of the cookie's http-only flag), it doesn't matter because they can just send all the needed commands, and the browser will happily send the JWT token along with those commands.
Now while the XSS-attack situation is arguably "game over" either way (whether local-storage or secured cookie), cookies are still a little better, because the attacker is only able to execute the attacks if/when the user has the website open in their browser.
This causes the following "annoyances" for the attacker:
"My XSS injection worked! Okay, time to collect private data on my boss and use it as blackmail. Dang it! He only ever logs in while I'm here at work. I'll have to prepare all my code ahead of time, and have it run within the three minutes he's on there, rather than getting to poke around into his data on the platform in a more gradual/exploratory way."
"My XSS injection worked! Now I can change the code to send all Bitcoin transfers to me instead! I don't have any particular target in mind, so I don't need to wait for anyone. Man though, I wish I could access the JWT token itself -- that way I could silently collect them all, then empty everyone's wallets all at once. With these cookie-protected JWTs, I may only be able to hijack a few dozen visitors before the devs find out and suspend transfers..."
"My XSS injection worked! This'll give me access to even the data that only the admins can see. Hmmm, unfortunately I have to do everything through the user's browser. I'm not sure there's a realistic way for me to download those 3gb files using this; I start the download, but there are memory issues, and the user always closes the site before it's done! Also, I'm concerned that client-side retransfers of this size might get detected by someone."
If the JWT is third-party:
In this case, it really depends on what the third-party JWTs allow the holder to do.
If all they do is let someone "access basic profile information" on each user, then it's not that bad if attackers can access it; some emails may leak, but the attacker could probably get that anyway by navigating to the user's "account page" where that data is shown in the UI. (having the JWT token just lets them avoid the "annoyances" listed in the previous section)
If, instead, the third-party JWTs let you do more substantial things -- such as have full access to their cloud-storage data, send out messages on third-party platforms, read private messages on third-party platforms, etc, then having access to the JWTs is indeed substantially worse than just being able to "send authenticated commands".
This is because, when the attacker can't access the actual JWT, they have to route all commands through your 1st-party server. This has the following advantages:
1.**Limited commands:**Because all the commands are going through your server, attackers can only execute the subset of commands that your server was built to handle. For example, if your server only ever reads/writes from a specific folder in a user's cloud storage, then the attacker has the same limitation. 1.**Easier detection:**Because all the commands are going through your server, you may be able to notice (through logs, sudden uptick in commands, etc.) that someone has developed an XSS attack. This lets you potentially patch it more quickly. (if they had the JWTs themselves, they could silently be making calls to the 3rd-party platforms, without having to contact your servers at all) 1.**More ways to identify the attacker:**Because the commands are going through your server, you know exactly when the commands are being made, and what ip-address is being used to make them. In some cases, this could help you identify who is doing the attacks. The ip-address is the most obvious way, though admittedly most attackers capable of XSS attacks would be aware enough to use a proxy.
A more advanced identification approach might be to, say, have a special message pop up that is unique for each user (or, at least, split into buckets), of such a nature that the attacker (when he loads up the website from his own account) will see that message, and try to run a new command based on it. For example, you could link to a "fake developer blog post" talking about some "new API" you're introducing, which allows users to access even more of their private data; the sneaky part is that the URL for that "new API" is different per user viewing the blog post, such that when the API is attempted to be used (against the victim), you know exactly who did it. Of course, this relies on the idea that the attacker has a "real account" on the site alongside the victim, and could be tempted/fooled by this sort of approach (eg. it won't work if the attacker knows you're onto him), but it's an example of things you can do when you can intercept all authenticated commands. 1.**More flexible controlling:**Lets say that you've just discovered that someone deployed an XSS attack on your site.
If the attackers have the 3rd-party JWTs themselves, your options are limited: you have to globally disable/reset your OAuth/JWT configuration for all 3rd-party platforms. This causes serious disruption while you try to figure out the source of the XSS attack, as no one is able to access anything from those 3rd-party platforms. (including your own server, since the JWT tokens it may have stored are now invalid)
If the JWT tokens are instead protected in http-only cookies, you have more options: You can simply modify your server to "filter out" any reads/writes that are potentially dangerous. In some cases added this "filtering" is a quick and easy process, allowing your site to continue in "read-only"/"limited" mode without disrupting everything; in other cases, things may be complex enough that it's not worth trusting the filter code for security. The point though is that you have more options.
For example, maybe you don't know for sure that someone has deployed an XSS attack, but you suspect it. In this case, you may not want to invalidate the JWT tokens of every user (including those your server is using in the background) simply on the suspicion of an XSS attack (it depends on your suspicion level). Instead, you can just "make things read-only for a while" while you look into the issue more closely. If it turns out nothing is wrong, you can just flip a switch and re-enable writes, without everyone having to log back in and such.
Anyway, because of these four benefits, I've decided to always store third-party JWTs in "secured cookies" rather than local storage. While currently the third-party JWTs have very limited scopes (such that it's not so big a deal if they are stolen), it's good future-proofing to do this, in case I'd like my app to request access to more privileged functionalities in the future (eg. access to the user's cloud storage).
**Note:**The four benefits above (for storing third-party JWTs in secured cookies) may also partially apply for first-party JWTs, if the JWTs are used as authentication by multiple backend services, and the domains/ip-addresses of these other servers/services are public knowledge. In this case, they are "equivalent to third-party platforms", in the sense that "http-only cookies" restrict the XSS attacker from sending direct commands to those other servers, bringing part of the benefits of the four points above. (it's not exactly the same, since you do at least control those other servers, so you can activate read-only mode for them and such -- but it'll still generally be more work than making those changes in just one place)
12条答案
按热度按时间ht4b089n1#
在大多数现代单页面应用程序中,我们确实必须将令牌存储在客户端的某个位置(最常见的用例--在页面刷新后保持用户登录)。
共有2个选项可供选择:Web存储(会话存储、本地存储)和客户端Cookie。这两个选项都被广泛使用,但这并不意味着它们非常安全。
Tom Abbott很好地总结了JWT sessionStorage and localStorage security:
Web存储(本地存储/会话存储)可通过同一域上的JavaScript访问。这意味着在您的站点上运行的任何JavaScript都将可以访问Web存储,因此可能容易受到跨站点脚本(XSS)攻击。简而言之,XSS是一种漏洞,攻击者可以在其中注入将在您的页面上运行的JavaScript。基本的XSS攻击试图通过表单输入注入JavaScript,攻击者将
<script>alert('You are Hacked');</script>
放入表单中,以查看它是否由浏览器运行并可被其他用户查看。为了防止XSS,常见的React是对所有不受信任的数据进行转义和编码。Reaction(主要)会为您做到这一点!这是一个很棒的discussion about how much XSS vulnerability protection is React responsible for。
但这并不能涵盖所有可能的漏洞!另一个潜在威胁是CDN上或外部基础设施上托管的JavaScript的使用。
汤姆又来了:
现代网络应用程序包括用于A/B测试、漏斗/市场分析和广告的第三方JavaScript库。我们使用像BPower这样的包管理器将其他人的代码导入到我们的应用程序中。
如果您使用的脚本中只有一个被攻破了,该怎么办?恶意的JavaScript可能会嵌入到页面上,Web存储也会受到影响。**这些类型的XSS攻击可以在他们不知情的情况下获取访问您站点的每个人的Web存储。**这可能是为什么许多组织建议不要在Web存储中存储任何有价值的东西或信任任何信息。这包括会话标识符和令牌。
因此,我的结论是,作为一种存储机制,Web存储在传输过程中不执行任何安全标准。无论谁阅读并使用Web存储,都必须尽其所能确保始终通过HTTPS发送JWT,而不是通过HTTP。
uyhoqukh2#
基本上,将JWT存储在本地存储中是可以的。
我认为这是一个很好的方式。如果我们谈论的是XSS,XSS使用CDN,这也是获得客户登录/通行证的潜在风险。将数据存储在本地存储至少可以防止CSRF攻击。
你需要同时意识到这两点,并选择你想要的东西。这两种攻击并不是你需要知道的全部,只需记住:你的整个应用程序的安全性只是和你的应用程序中最不安全的一点一样。
再次存储是可以的,容易受到XSS、CSRF等的攻击。不是吗
ufj5ltwl3#
我知道这是一个老问题,但根据@mikejones 1477的说法,现代前端库和框架避开了文本,为您提供了针对XSS的保护。Cookie不是一种使用凭据的安全方法的原因是,当本地存储阻止CSRF时,Cookie不会阻止CSRF(还要记住,Cookie也可以由JavaScript访问,因此XSS在这里不是大问题),this answer resume why。
将身份验证令牌存储在本地存储中并手动将其添加到每个请求中以防止CSRF的原因就是这个关键字:手动。由于浏览器不会自动发送该身份验证令牌,因此如果我访问
evil.example
并设法发送帖子http://example.com/delete-my-account
,它将无法发送我的身份验证令牌,因此该请求将被忽略。当然,HTTPOnly是圣杯,但你不能从reactjs或你身边的任何js框架访问,仍然有CSRF漏洞。我的建议是本地存储,或者如果你想使用Cookie,确保像Django那样实现一些CSRF问题的解决方案。
关于CDN,确保你没有使用一些奇怪的CDN,例如像Google或Bootstrap Provide这样的CDN,是由社区维护的,不包含恶意代码,如果你不确定,你可以自由审查。
3pvhb19x4#
所有建议不要存储在本地存储的答案都让我感到不安,因为这很容易受到XSS攻击或恶意库的攻击。其中一些问题甚至进入了冗长的讨论,尽管答案相当小/简单,我很快就会谈到。
这就相当于说“不要用煎锅做饭,因为如果你某天晚上喝醉了,决定要炸,你会把自己和房子都烧焦的。”如果JWT由于XSS攻击或恶意库而泄露,则站点所有者有一个更大的问题:他们的站点容易受到XSS攻击或正在使用恶意库。
答案是:如果你确信你的网站没有这些漏洞,那就去做吧。
参考:https://auth0.com/docs/security/data-security/token-storage#browser-local-storage-scenarios
ogsagwnx5#
看待这一问题的一种方式是考虑风险或伤害的程度。
您是否正在构建一款没有用户的应用程序,PoC/MVP?你是一家需要快速进入市场并测试你的应用程序的初创公司吗?如果是,我可能只会实施最简单的解决方案,并继续专注于寻找适合产品市场的产品。使用本地存储通常更容易实现。
您是在构建日活跃用户众多的应用程序的v2,还是构建个人/企业严重依赖的应用程序?被黑客攻击是否意味着恢复的空间很小或没有?如果是这样的话,我会仔细研究您的依赖关系,并考虑将令牌信息存储在仅限http的cookie中。
同时使用本地存储和cookie/会话存储各有优缺点。
正如第一个答案所述:如果您的应用程序有XSS漏洞,这两个漏洞都不能保护您的用户。由于大多数现代应用程序都有十几个或更多不同的依赖项,因此要保证应用程序的一个依赖项不受XSS攻击变得越来越困难。
如果您的应用程序确实存在XSS漏洞,并且黑客已经能够利用它,则黑客将能够代表您的用户执行操作。黑客可以通过从本地存储中检索令牌来执行GET/POST请求,或者如果令牌存储在仅限http的Cookie中,则可以执行POST请求。
将令牌存储在本地存储中的唯一缺点是黑客将能够读取您的令牌。
5gfr0r5j6#
如果使用CDN的,则不安全:
恶意的JavaScript可能会嵌入到页面上,Web存储也会受到影响。这些类型的XSS攻击可以在他们不知情的情况下获取访问您站点的每个人的Web存储。这可能就是为什么许多组织建议不要在网络存储中存储任何有价值的东西或信任任何信息的原因。这包括会话标识符和令牌。
通过stormpath
您从外部需要的任何脚本都可能被破坏,并可能从您客户端的存储中获取任何JWTS,并将个人数据发送回攻击者的服务器。
flmtquvp7#
本地存储被设计成可以被Java脚本访问,因此它不提供任何XSS保护。正如在其他答案中提到的,有许多可能的方法来进行XSS攻击,默认情况下,本地存储不受保护。
但是,Cookie具有安全标志,可防止XSS和CSRF攻击。HttpOnly标志阻止客户端脚本访问Cookie,Secure标志只允许浏览器通过SSL传输Cookie,SameSite标志确保Cookie只发送到源。虽然我刚才检查了一下,SameSite目前只在Opera和Chrome中支持,所以为了保护自己免受CSRF的攻击,最好使用其他策略。例如,在另一个Cookie中发送一个加密令牌和一些公共用户数据。
因此,Cookie是存储身份验证数据的更安全的选择。
1rhkuytd8#
One thing to keep in mind is whether the JWTs are:
If the JWT is first-party:
Then it doesn't matter that much whether you store the JWT in local storage, or a secured cookie (ie.
HttpOnly
,SameSite=strict
, andsecure
) [assuming your site is already using HTTPS, which it should].This is because, assuming an XSS attack succeeds (ie. an attacker was able to insert Javascript code through a JS dependency that is now running on all visitor browsers), it's "game over" anyway; all the commands which were meant to be secured by the "JWT token verifications", can now be executed by the attacker just by having the script they've inserted into the frontend JS call all the needed endpoints. Even though they can't read the JWT token itself (because of the cookie's http-only flag), it doesn't matter because they can just send all the needed commands, and the browser will happily send the JWT token along with those commands.
Now while the XSS-attack situation is arguably "game over" either way (whether local-storage or secured cookie), cookies are still a little better, because the attacker is only able to execute the attacks if/when the user has the website open in their browser.
This causes the following "annoyances" for the attacker:
If the JWT is third-party:
In this case, it really depends on what the third-party JWTs allow the holder to do.
If all they do is let someone "access basic profile information" on each user, then it's not that bad if attackers can access it; some emails may leak, but the attacker could probably get that anyway by navigating to the user's "account page" where that data is shown in the UI. (having the JWT token just lets them avoid the "annoyances" listed in the previous section)
If, instead, the third-party JWTs let you do more substantial things -- such as have full access to their cloud-storage data, send out messages on third-party platforms, read private messages on third-party platforms, etc, then having access to the JWTs is indeed substantially worse than just being able to "send authenticated commands".
This is because, when the attacker can't access the actual JWT, they have to route all commands through your 1st-party server. This has the following advantages:
1.**Limited commands:**Because all the commands are going through your server, attackers can only execute the subset of commands that your server was built to handle. For example, if your server only ever reads/writes from a specific folder in a user's cloud storage, then the attacker has the same limitation.
1.**Easier detection:**Because all the commands are going through your server, you may be able to notice (through logs, sudden uptick in commands, etc.) that someone has developed an XSS attack. This lets you potentially patch it more quickly. (if they had the JWTs themselves, they could silently be making calls to the 3rd-party platforms, without having to contact your servers at all)
1.**More ways to identify the attacker:**Because the commands are going through your server, you know exactly when the commands are being made, and what ip-address is being used to make them. In some cases, this could help you identify who is doing the attacks. The ip-address is the most obvious way, though admittedly most attackers capable of XSS attacks would be aware enough to use a proxy.
A more advanced identification approach might be to, say, have a special message pop up that is unique for each user (or, at least, split into buckets), of such a nature that the attacker (when he loads up the website from his own account) will see that message, and try to run a new command based on it. For example, you could link to a "fake developer blog post" talking about some "new API" you're introducing, which allows users to access even more of their private data; the sneaky part is that the URL for that "new API" is different per user viewing the blog post, such that when the API is attempted to be used (against the victim), you know exactly who did it. Of course, this relies on the idea that the attacker has a "real account" on the site alongside the victim, and could be tempted/fooled by this sort of approach (eg. it won't work if the attacker knows you're onto him), but it's an example of things you can do when you can intercept all authenticated commands.
1.**More flexible controlling:**Lets say that you've just discovered that someone deployed an XSS attack on your site.
If the attackers have the 3rd-party JWTs themselves, your options are limited: you have to globally disable/reset your OAuth/JWT configuration for all 3rd-party platforms. This causes serious disruption while you try to figure out the source of the XSS attack, as no one is able to access anything from those 3rd-party platforms. (including your own server, since the JWT tokens it may have stored are now invalid)
If the JWT tokens are instead protected in http-only cookies, you have more options: You can simply modify your server to "filter out" any reads/writes that are potentially dangerous. In some cases added this "filtering" is a quick and easy process, allowing your site to continue in "read-only"/"limited" mode without disrupting everything; in other cases, things may be complex enough that it's not worth trusting the filter code for security. The point though is that you have more options.
For example, maybe you don't know for sure that someone has deployed an XSS attack, but you suspect it. In this case, you may not want to invalidate the JWT tokens of every user (including those your server is using in the background) simply on the suspicion of an XSS attack (it depends on your suspicion level). Instead, you can just "make things read-only for a while" while you look into the issue more closely. If it turns out nothing is wrong, you can just flip a switch and re-enable writes, without everyone having to log back in and such.
Anyway, because of these four benefits, I've decided to always store third-party JWTs in "secured cookies" rather than local storage. While currently the third-party JWTs have very limited scopes (such that it's not so big a deal if they are stolen), it's good future-proofing to do this, in case I'd like my app to request access to more privileged functionalities in the future (eg. access to the user's cloud storage).
**Note:**The four benefits above (for storing third-party JWTs in secured cookies) may also partially apply for first-party JWTs, if the JWTs are used as authentication by multiple backend services, and the domains/ip-addresses of these other servers/services are public knowledge. In this case, they are "equivalent to third-party platforms", in the sense that "http-only cookies" restrict the XSS attacker from sending direct commands to those other servers, bringing part of the benefits of the four points above. (it's not exactly the same, since you do at least control those other servers, so you can activate read-only mode for them and such -- but it'll still generally be more work than making those changes in just one place)
p4tfgftt9#
难道本地存储和HTTPOnly Cookie都不能接受吗?关于受攻击的第三方库,我所知道的唯一可以减少/防止敏感信息被盗的解决方案是强制执行Subresource Integrity。
子资源完整性(SRI)是一种安全功能,使浏览器能够验证它们获取的资源(例如,从CDN)是否在没有意外操作的情况下交付。它的工作方式是允许您提供获取的资源必须匹配的加密散列。
只要受攻击的第三方库在你的网站上处于活动状态,键盘记录器就可以开始收集用户名、密码等信息,以及你在网站上输入的任何其他信息。
HTTPOnly Cookie将阻止来自另一台计算机的访问,但不会阻止黑客操纵用户的计算机。
cgh8pdjw10#
TLDR;
两者都有效,但是使用cookie和HTTpOnly要比使用本地存储安全得多,因为XSS引入的任何恶意脚本代码都可以读取本地存储。
l7mqbcuq11#
有一个由Dr. Philippe De Ryck编写的有用的article,它提供了对漏洞,特别是XSS的真实影响的洞察。
这篇文章让人大开眼界!
简而言之,开发人员主要关心的应该是保护Web应用程序免受XSS的攻击,而不应该太担心使用了什么类型的存储区域。
菲利普博士建议采取以下3个步骤:
1.不要太担心存储区域。在本地存储区域中保存访问令牌将为开发人员节省大量时间来开发应用程序的下一阶段。
1.检查您的应用程序是否存在XSS漏洞。执行完整的代码审查,并了解如何在模板框架的范围内避免XSS。
1.构建XSS纵深防御机制。了解如何进一步锁定您的应用程序。例如,使用内容安全策略(CSP)和HTML5沙盒。
请记住,一旦您容易受到XSS的攻击,那么游戏就结束了!
jogvjijk12#
只要对令牌进行加密,就可以安全地将令牌存储在本地存储中。下面是一个压缩的代码片段,展示了多种方法之一。
然后,在使用令牌之前使用
PRIVATE_KEY_STORED_IN_ENV_FILE
对其进行解密