用ReactJS在localStorage中存储JWT安全吗?

我目前正在使用ReactJS构建一个单页应用程序。

我读到不使用localStorage的原因之一是因为XSS漏洞。

既然React会转义所有用户输入,那么现在使用localStorage是否安全?

182540 次浏览

如果使用CDN's:
是不安全的

恶意JavaScript可以嵌入到页面中,Web存储就会受到威胁。这些类型的XSS攻击可以在不知情的情况下获取访问您站点的每个人的Web存储。这可能就是为什么许多组织建议不要在网络存储中存储任何有价值的东西或信任任何信息的原因。这包括会话标识符和令牌。

通过stormpath

您从外部需要的任何脚本都可能被破坏,并可能从客户机的存储中获取任何JWTS,并将个人数据发送回攻击者的服务器。

在大多数现代单页应用程序中,我们确实必须将令牌存储在客户端某处(最常见的用例——在页面刷新后保持用户登录)。

总共有2个选项可用:Web Storage(会话存储,本地存储)和客户端cookie。这两种方法都被广泛使用,但这并不意味着它们非常安全。

Tom Abbott很好地总结了JWT sessionStorage和localStorage安全:

Web Storage (localStorage/sessionStorage)可以在同一个域中通过JavaScript访问。这意味着在你的网站上运行的任何JavaScript都可以访问web存储,并且由于这个容易受到跨站脚本(XSS)攻击. js。简而言之,XSS是一种漏洞,攻击者可以在其中注入将在您的页面上运行的JavaScript。基本的XSS攻击试图通过表单输入注入JavaScript,攻击者将<script>alert('You are Hacked');</script>放入表单中,以查看它是否由浏览器运行,是否可以被其他用户查看。

为了防止XSS,常见的响应是转义和编码所有不可信的数据。React(主要)为您做这些!这是一个很棒的React在多大程度上负责XSS漏洞保护的讨论

但这并没有涵盖所有可能的漏洞!另一个潜在的威胁是JavaScript托管在cdn或外部基础设施上的使用

汤姆说:

现代网络应用包括用于A/B测试、漏斗/市场分析和广告的第三方JavaScript库。我们使用包管理器(如Bower)将其他人的代码导入到我们的应用中。

如果您使用的脚本中只有一个被破坏了怎么办?恶意JavaScript可以嵌入到页面中,Web存储就会受到威胁。这可能就是为什么一堆组织建议不要在网络存储中存储任何有价值的东西或信任任何信息。这包括会话标识符和令牌。

因此,我的结论是,作为一种存储机制,Web storage 在传输过程中没有强制执行任何安全标准。任何阅读Web Storage并使用它的人都必须尽职尽责,以确保始终通过HTTPS而不是HTTP发送JWT。

Localstorage被设计成可以通过javascript访问,所以它不提供任何XSS保护。正如在其他回答中提到的,有很多可能的方式来进行XSS攻击,默认情况下,本地存储不受保护。

但是,cookie具有安全标志,可以防止XSS和CSRF攻击。HttpOnly标志阻止客户端javascript访问cookie, Secure标志只允许浏览器通过ssl传输cookie, SameSite标志确保cookie只发送到原点。虽然我刚刚检查了一下,SameSite目前只支持Opera和Chrome,所以为了防止CSRF,最好使用其他策略。例如,在另一个cookie中发送带有一些公共用户数据的加密令牌。

因此,cookie是存储身份验证数据的更安全的选择。

基本上可以将JWT存储在localStorage中。

我认为这是一个好方法。 如果我们谈论的是XSS, XSS使用CDN,这也是一个潜在的风险,获得您客户的登录/通行证。将数据存储在本地至少可以防止CSRF攻击。< / p >

你需要了解这两点,然后选择你想要的。这两种攻击都不是你所需要注意的,只要记住:你的整个应用程序的安全性仅与你的应用程序的最不安全的点一样。

再次重申,存储是OK的,易受XSS, CSRF,…不是

我知道这是一个老问题,但根据@mikejones1477所说,现代前端库和框架转义文本,为您提供对XSS的保护。cookie不是使用凭证的安全方法的原因是,当localStorage阻止CSRF时,cookie不会阻止CSRF(还要记住,cookie也可以通过JavaScript访问,所以XSS不是这里的大问题),这是为什么?

在本地存储中存储身份验证令牌并手动将其添加到每个请求中以防止CSRF的原因是关键字:手动。由于浏览器不会自动发送认证令牌,如果我访问evil.example,它设法发送一个POST http://example.com/delete-my-account,它将无法发送我的认证令牌,因此该请求被忽略。

当然httpOnly是圣杯,但你不能从reactjs或任何js框架访问,你仍然有CSRF漏洞。我的建议是localstorage,或者如果你想使用cookie,确保实现一些解决方案的CSRF问题就像Django一样

关于CDN,确保你没有使用一些奇怪的CDN,例如谷歌或bootstrap提供的CDN,由社区维护,不包含恶意代码,如果你不确定,你可以自由审查。

localStorage和httpOnly cookie都不能接受吗?关于一个妥协的第三方库,我所知道的唯一的解决方案,将减少/防止敏感信息被窃取将强制子资源的完整性

子资源完整性(SRI)是一个安全特性 浏览器验证它们获取的资源(例如从CDN) 交付时没有意外的操作。它通过允许 您可以提供所获取资源必须的加密散列 比赛。< / p >

只要受感染的第三方库在您的网站上是活跃的,键盘记录程序就可以开始收集信息,如用户名,密码,以及您输入到网站的任何其他内容。

httpOnly cookie将阻止来自另一台计算机的访问,但不会阻止黑客操纵用户的计算机。

看待这个问题的一种方法是考虑风险或伤害的水平。

你是否正在创造一款没有用户的应用,即POC/MVP?你是一家需要快速进入市场并测试应用程序的初创公司吗?如果是,我可能只会实施最简单的解决方案,并继续专注于寻找适合市场的产品。使用localStorage,因为它通常更容易实现。

你是在开发一款拥有大量日活跃用户的应用的v2版本,还是一款人们/企业非常依赖的应用?被黑客攻击是否意味着几乎没有恢复空间?如果是这样,我会仔细研究一下您的依赖关系,并考虑将令牌信息存储在仅http的cookie中。

使用localStorage和cookie/session存储都有各自的优缺点。

正如第一个答案所述:如果您的应用程序有XSS漏洞,那么两者都不能保护您的用户。由于大多数现代应用程序都有十几个或更多不同的依赖项,因此越来越难以保证应用程序的某个依赖项不受XSS攻击。

如果您的应用程序确实存在XSS漏洞,并且黑客已经能够利用它,那么黑客将能够代表您的用户执行操作。黑客可以通过从localStorage检索令牌来执行GET/POST请求,或者如果令牌存储在http-only cookie中,则可以执行POST请求。

在本地存储中存储令牌的唯一缺点是黑客将能够读取您的令牌。

只要加密,将令牌存储在localStorage中是安全的。下面是一个压缩的代码片段,展示了许多方法中的一种。

    import SimpleCrypto from 'simple-crypto-js';


const saveToken = (token = '') => {
const encryptInit = new SimpleCrypto('PRIVATE_KEY_STORED_IN_ENV_FILE');
const encryptedToken = encryptInit.encrypt(token);


localStorage.setItem('token', encryptedToken);
}

然后,在使用令牌之前,使用PRIVATE_KEY_STORED_IN_ENV_FILE解密它

要记住的一件事是jwt是否:

  • 第一方。简单地访问您自己的服务器命令)
  • 第三方;谷歌,Facebook, Twitter等的JWT)

如果智威汤逊是第一方:

然后,不管你是将JWT存储在本地存储中,还是存储在安全的cookie中(例如。HttpOnlySameSite=strict,和secure)[假设你的网站已经在使用HTTPS,它应该]。

这是因为,假设XSS攻击成功(即XSS攻击成功)。一个攻击者能够通过JS依赖插入Javascript代码,现在在所有访问者浏览器上运行),这是“游戏结束”;无论如何;所有原本由“JWT令牌验证”保护的命令,现在都可以由攻击者执行,只需让他们插入前端JS的脚本调用所有需要的端点。即使他们不能读取JWT令牌本身(因为cookie的http-only标志),这也没关系,因为他们可以发送所有需要的命令,浏览器会很高兴地将JWT令牌与这些命令一起发送。

现在,虽然xss攻击的情况可以说是“游戏结束”;无论哪种方式(无论是本地存储还是安全cookie), cookie仍然是更好,因为攻击者只有在用户在浏览器中打开网站时才能执行攻击。

这会导致以下“烦恼”:对于攻击者:

  1. “我的XSS注射成功了!”好吧,是时候收集我老板的私人数据然后拿来勒索了。见鬼了!他只在我上班的时候登录。我必须提前准备好我所有的代码,让它在他在平台上的三分钟内运行,而不是以一种更渐进/探索的方式在平台上戳他的数据。”
  2. “我的XSS注射成功了!”现在我可以更改代码,将所有比特币转账发送给我!我心中没有任何特定的目标,所以我不需要等任何人。天啊,我希望我能访问JWT代币本身——这样我就可以默默地收集它们,然后一次性清空每个人的钱包。有了这些受cookie保护的jwt,我可能只能在开发人员发现并暂停传输之前劫持几十个访客……”
  3. “我的XSS注射成功了!”这样我就能访问只有管理员才能看到的数据了。嗯,不幸的是,我必须通过用户的浏览器来做所有的事情。我不确定是否有可行的方法来下载3gb的文件;我开始下载,但有内存问题,用户总是关闭网站之前完成!此外,我担心这种规模的客户端重转可能会被某人发现。”

如果JWT是第三方:

在这种情况下,这实际上取决于第三方jwt允许持有者做什么。

如果他们所做的只是让别人“访问基本的档案信息”;在每个用户身上,如果攻击者可以访问它,那么它就不是那么糟糕;一些电子邮件可能会泄露,但攻击者很可能通过导航到用户的“帐户页面”而获得这些信息。数据显示在UI中。(拥有JWT令牌只是让他们避免了“烦恼”;在前一节中列出)

相反,如果第三方jwt让你做更实质性的事情——比如完全访问他们的云存储数据,在第三方平台上发送消息,在第三方平台上读取私人消息,等等,那么访问jwt确实比仅仅能够“发送经过身份验证的命令”要糟糕得多。

这是因为,当攻击者无法访问实际的JWT时,他们必须通过您的第一方服务器路由所有命令。这样做有以下优点:

  1. 因为所有的命令都要经过你的服务器,攻击者只能执行你的服务器要处理的命令的子集。例如,如果您的服务器只读取/写入用户云存储中的特定文件夹,那么攻击者也有相同的限制。

  2. 因为所有的命令都通过你的服务器,你可能会注意到(通过日志,命令的突然增加等)有人已经开发了XSS攻击。这可以让你更快地修补它。(如果他们自己有jwt,他们可以默默地调用第三方平台,而不需要联系你的服务器)

  3. 因为命令是通过你的服务器,你确切地知道命令是什么时候发出的,以及使用什么ip-address来发出它们。在某些情况下,这可以帮助您确定谁在进行攻击。ip-address是最明显的一种方式,但不可否认,大多数能够进行XSS攻击的攻击者都知道使用代理。

    一种更高级的识别方法可能是,比如,弹出一个特殊的消息,该消息对每个用户来说都是唯一的(或者,至少是分成几个桶),这样攻击者(当他从自己的帐户加载网站时)就会看到该消息,并尝试基于它运行一个新命令。例如,你可以链接到一个“假开发者博客文章”。谈论一些“新的api”;你所介绍的,允许用户访问更多的私人数据;狡猾的部分是那个“新api”的URL;不同的用户浏览博客帖子,这样当API试图被使用(针对受害者)时,你确切地知道是谁做的。当然,这依赖于攻击者拥有“真实账户”的想法。在受害者旁边的网站上,并可能被这种方法所诱惑/愚弄(例如。如果攻击者知道你盯上了他,它就不会起作用),但这是当你可以拦截所有经过身份验证的命令时可以做的事情的一个例子。

  4. 让我们假设你刚刚发现有人在你的网站上部署了XSS攻击。

    如果攻击者自己拥有第三方JWT,那么您的选择是有限的:您必须全局禁用/重置所有第三方平台的OAuth/JWT配置。当你试图找出XSS攻击的来源时,这会导致严重的中断,因为没有一个人能够从这些第三方平台访问任何东西。(包括您自己的服务器,因为它可能存储的JWT令牌现在无效)

    如果JWT令牌在http-only cookie中被保护,您有更多的选择:您可以简单地修改您的服务器以“过滤掉”。任何有潜在危险的读/写。在某些情况下增加了这个“过滤”;是一个快速和简单的过程,允许您的网站继续在“只读”;/“有限”;模式,不破坏一切;在其他情况下,事情可能非常复杂,不值得信任过滤器代码的安全性。关键是你有更多的选择。

    例如,也许你不知道可以肯定的是有人部署了XSS攻击,但你怀疑它。在这种情况下,您可能不希望仅仅因为怀疑XSS攻击(这取决于您的怀疑级别)就使每个用户(包括服务器在后台使用的那些用户)的JWT令牌失效。相反,你可以“让内容只读一段时间”。当你更仔细地研究这个问题时。如果结果没有问题,您只需打开开关并重新启用写操作,而不需要所有人重新登录等等。

无论如何,由于这四个好处,我决定始终将第三方jwt存储在“安全cookie”中。而不是本地存储。虽然目前第三方jwt的作用域非常有限(因此如果它们被窃取也不是什么大问题),但这样做是很好的未来证明,以防我希望我的应用程序在未来请求访问更多特权功能(例如。访问用户的云存储)。

注意:上述四个好处(用于将第三方jwt存储在安全的cookie中)也可以用于第一方jwt, 如果 jwt被多个后端服务用作身份验证,这些其他服务器/服务的域/ip地址是公共知识。在这种情况下,它们“等同于第三方平台”,因为“仅http cookie”;限制XSS攻击者发送直接命令到那些其他服务器,给的一部分带来上述四点的好处。(这并不完全相同,因为你至少控制了那些其他的服务器,所以你可以为它们激活只读模式等等——但它通常仍然比在一个地方做这些改变更麻烦)

我对所有建议不要存储在本地存储的答案感到不安,因为这很容易受到XSS攻击或恶意库的攻击。其中一些问题甚至会进入冗长的讨论,尽管答案非常小/简单,我很快就会讲到。

这就相当于说“不要用煎锅做饭,因为如果你某天晚上喝醉了,决定用煎锅做饭,你会把自己和房子都烧了”。 如果jwt由于XSS攻击或恶意库而泄露,那么网站所有者就有更大的问题:他们的网站容易受到XSS攻击或正在使用恶意库

答案是:如果你确信你的网站不存在这些漏洞,那就去做吧。

裁判:https://auth0.com/docs/security/data-security/token-storage#browser-local-storage-scenarios

TLDR;

两者都可以工作,但是使用httpOnly是更安全的方式而不是使用localStorage,因为XSS引入的任何恶意javascript代码都可以读取localStorage。

菲利普·德·雷克博士编写了一个有用的文章,它可以深入了解漏洞(特别是XSS)的真正影响。

这篇文章让人大开眼界!

简而言之,开发人员最关心的应该是保护web应用程序不受XSS的影响,而不应该太担心使用哪种类型的存储区域。

菲利普医生建议采取以下3个步骤:

  1. 不要太担心存储区域。将访问令牌保存在localStorage区域将为开发人员节省大量开发应用程序下一阶段的时间。

  2. 检查你的应用程序是否存在XSS漏洞。执行一次完整的代码检查,了解如何在模板框架范围内避免XSS。

  3. 建立针对XSS的纵深防御机制。了解如何进一步锁定应用程序。例如,利用内容安全策略(CSP)和HTML5沙箱。

记住,一旦你被XSS攻击了,游戏就结束了!