如何在具有去耦API的单页应用程序(SPA)中安全地实现身份验证



我一直在研究如何在单页应用程序(SPA)中最好地存储身份验证令牌。关于SO这个话题,目前存在一些争论,但据我所见,没有一个能提供具体的解决方案。

昨天和今天花了很多时间在互联网上搜索答案,我发现了以下内容:

  • 本地存储API我发现一些基本指南建议使用localStorage(尽管许多人理所当然地建议不要使用它)。我不喜欢这种方法,因为在XSS攻击的情况下,存储在localStorage中的数据可能会被访问。

  • 网络工作者如果令牌存储在web worker中,则在打开新选项卡时,用户将不会登录。这造成了不合标准和令人困惑的用户体验。

  • 关闭。和Web Workers一样——没有持久性。

  • HttpOnly Cookies一方面,我读到这可以防止XSS。然而,另一方面,这难道不意味着我们现在必须处理CSRF吗?然后是一个新的争论:如何使用SPA+REST API实现CSRF令牌?

其他人过得怎么样?

我很高兴你问这个问题。前端反复出现的关于oauth2的模因确实污染了辩论,很难找到事实信息。

首先,关于一些被排除在外的选项,我建议重新考虑:如果您在多个选项卡上需要相同的身份验证,您仍然可以使用任何将令牌存储在窗口范围内的选项,但可以单独管理令牌并在页面刷新(静默刷新,因此标准提示=无流)。这打开了一些选项:服务工作者、网络工作者、闭包。。。的确,其中一些最初并不是为了这个目的,但它很好地解决了这个问题。这也解决了关于刷新令牌的一系列竞争条件(它们只能使用一次,因此每个选项卡都有一个可以解决一系列问题)。

话虽如此,以下是选项:

  • 本地存储:如果XSS攻击成功,令牌可能会被盗。XSS=无论如何游戏结束IMO(在这种情况下,没有黑客会关心你的代币,这是不需要的)。与cookie的典型小时/天有效性相比,它也可以通过具有短暂的令牌来减轻。在任何情况下,都建议使用短命令牌。

    现在,对于一些人来说,XSS情况下被盗的代币似乎是一个重要问题,所以让我们看看其他选项。

  • 会话存储:与本地存储相同的缺点(XSS可能导致会话泄漏),引入了自己的CSRF问题,但也解决了其他一些问题(刷新…)

  • 网络工作者:这实际上是一个不错的解决方案。如果XSS在应用程序的随机部分成功,它将无法窃取令牌。理论上,如果可以注入一些在身份验证时运行的脚本(身份验证代码或令牌交换),它也可能被利用。。。但所有流都是如此,包括cookie/会话。

  • 闭包:与网络工作者相同。不那么孤立(更容易被一个会偷走你代币的东西取代)。

  • 服务人员:在我看来,这是一个理想的解决方案。易于实现(您只需截取获取请求并在几行代码中添加您的令牌)。XSS无法注入。你甚至可以争辩说,它实际上是针对那个确切的用例。它还解决了一个页面上有多个应用程序的情况(它们共享一个服务工作者,在需要时添加令牌),其他选项都不能很好地解决这个问题。唯一的缺点是:浏览器可以终止它,您需要实现一些东西来延长它的寿命(但有一种记录在案的标准方法)。

  • HttpOnly Cookies:简而言之,您将过渡到传统的服务器端web应用程序(至少在某些部分),它不再是一个具有标准oidc或aouth2的独立SPA。这是一种选择(多年来一直不是我的选择),但不应该以代币存储为动机,因为有一些选择甚至是安全的,而且可以说是更好的。

结论:我的建议是只使用本地/会话存储。成功的XSS可能会让您的工作或客户付出代价(提示:当他们可以调用pay(5000000, lulzsecAccount)API时,没有人对您的代币感兴趣)。

如果你对代币存储很挑剔,服务人员是IMO.的最佳选择

解决方案是HttpOnly SameSite Cookie。

在问题中,您正确地注意到HttpOnly可防止XSS。CCD_ 5反过来保护不受CSRF的影响。所有现代浏览器都支持这两个选项。

这比其他解决方案简单和安全了几个数量级。在API上设置很容易,而且对SPA完全透明。安全性是内置的。

具体解决方案:实际身份验证可以由API或重定向到API的外部提供商完成。然后,当登录时,API创建一个JWT令牌并将其存储在HttpOnly SameSiteCookie中。您可以在NestJS starter上使用NestJS看到这一点,如:用于社交登录的NestJS中的OAuth2(谷歌、脸书、推特等)所述。

一个限制是API和SPA必须在相同的域上。

对于存储令牌客户端,另一篇文章非常全面。

您的比较有点令人困惑。。。当您谈到SPA身份验证时,您需要一个允许您存储身份验证令牌的解决方案。Web Workers和Closures的主要用途是运行代码,而不是存储身份验证令牌。你可以把它写为一个硬编码的变量,但这不是它的目的,这就是为什么一个新的标签无法识别它的原因

因此,我们只剩下本地存储和会话Cookie。在SPA和客户端渲染之前,我们过去只有服务器端渲染和cookie。这就是HTTPOnly的发明,目的是让窃取会话ID和用户身份变得更加困难。当客户端呈现被发明时,无状态API被发明了。这些API不使用会话ID,而是使用令牌,例如JWT。这意味着服务器不会在由会话ID标识的会话中保存任何关于用户的信息。相反,JWT令牌在令牌本身中包含已签名的信息,服务器不会记住任何内容,并在每个请求中重新验证用户。还有一种将令牌保存在服务器端的NoSQL数据库(如Redis)中的混合方法,以加快身份验证过程。

但底线是HTTPOnly cookie与使用会话的有状态API一起使用,LocalStorage与使用令牌的无状态API一起用。你也可以反过来使用它(带有令牌的cookie,带有会话的LocalStorage),但它的";不太自然";。

至于XSS,cookie中的HttpOnly机制会让攻击者的生活更加艰难,但即使使用了它,攻击者仍然可以通过网络钓鱼等方式造成大量损害。因此,这并不重要,因为这只是一种补偿控制,而不是任何方式的主要缓解措施,因此您可以安全地使用LocalStorage。此外,cookies容易受到CSRF攻击,而LocalStorage则不然。因此,这两种选择都是有效的。

你可以在这里阅读更多关于它的信息:localStorage、sessionStorage、session和cookie之间有什么区别?

最新更新