为啥 auth0 建议不要在 localStorage 中存储令牌?
Posted
技术标签:
【中文标题】为啥 auth0 建议不要在 localStorage 中存储令牌?【英文标题】:Why is auth0 recommending not to store tokens in localStorage?为什么 auth0 建议不要在 localStorage 中存储令牌? 【发布时间】:2020-04-14 01:59:23 【问题描述】:Auth0 提供了描述身份验证最佳实践的广泛资源列表。其中有源源不断的建议不要使用 localStorage
作为存储 (JWT) 令牌的手段。
我发现这些点有几个问题:
强烈假设 JWT 令牌是攻击者不得访问的敏感信息(通过 XSS) 从我的角度来看,访问令牌本身不会扩大攻击范围。如果攻击者可以控制受害者的浏览器,他们就可以使用令牌从受影响的浏览器本身执行调用。 访问令牌通常具有相当短的到期时间,并且可以固定在浏览器上,从而减少在 XSS 上下文之外使用它们的机会。 Auth0 建议使用他们的 SDK 中的auth0.getTokenSilently()
来获取令牌,但据我所知,攻击者应该没有任何理由不能自己调用此方法(即注入另一个 sdk 脚本,使用现有的客户端密钥,只需从那里调用 getToken),从而以几乎与存储在 localStorage
中的方式相同的方式获取令牌
我知道 XSS 无法访问令牌的唯一方法基本上是使用 httpOnly cookie,但这会自行创建新向量 (CSRF),并且仍然不会阻止攻击者从内部调用 api受影响的浏览器。
所以我完全同意 OWASP 的建议,不要在localStorage
中存储敏感数据,我绝不会想到在其中存储信用卡号或密码甚至个人数据。但这只是因为这些东西会让攻击者扩大他们的攻击范围(访问银行账户,尝试在其他应用程序中重用用户密码等)。但我很难了解 accessTokens 是如何受此影响的。
【问题讨论】:
【参考方案1】:虽然我对 Auth0 的实现、功能和设计决策了解不多,但从我对 OAuth2 和安全性的一般理解来看,我可以尝试将这些点联系起来。
单个推荐本身并不能提供足够的安全性或所需的功能,但是当与其他相关推荐组合使用时,我们可以实现所需的安全性和行为级别。
让我们回顾一下你提出的观点。
从我的角度来看,访问令牌本身并不会扩大攻击范围。如果攻击者可以控制受害者的浏览器,他们可以使用令牌从受影响的浏览器本身执行调用
localstorage
的问题是:
localStorage
和 sessionStorage
不跨子域共享。这是 SSO 功能的显示停止器(有一些使用 iframe
的解决方案,但这些看起来更像是解决方法,而不是一个好的解决方案。当使用响应标头 X-Frame-Options 来避免使用 iframe
进行点击劫持攻击时, iframe
的任何解决方案都是不可能的)
XSS 可以将访问和/或刷新令牌发送到攻击者控制的远程服务器,从而允许攻击者冒充用户
注意:第 2 点中提到的漏洞可以通过使用Sender Constrained Access Tokens 方法来缓解,其中客户端必须证明他们确实拥有令牌。另一种选择是 OWASP 中提到的fingerprint approach,它需要一个 cookie。但是,似乎 Auth0 没有实现任何这些。因此,避免localstorage
的建议是有道理的。
Auth0 建议使用他们 SDK 中的 auth0.getTokenSilently() 来获取令牌,但据我所知,攻击者应该没有任何理由不能自己调用此方法
正确。这就是为什么
-
我们首先需要通过关注OWASP XSS prevention guidelines 来降低 XSS 的风险。
此外,
getTokenSilently()
方法要求您在仪表板的 API 设置中启用 Allow Skipping User Consent
。虽然我没有看到具体的指导方针,但我认为如果您将令牌存储在 cookie 中,则不需要启用此选项,从而消除了滥用该方法的任何可能性。
我知道 XSS 无法访问令牌的唯一方法基本上是使用 httpOnly cookie,但这会自行创建新向量 (CSRF),并且仍然不会阻止攻击者从内部调用 api受影响的浏览器
没错。但您可以通过以下一种或多种方法来缓解这种情况:
-
使用
SameSite
cookie。请参阅文档here。如果浏览器不支持SameSite
cookie,请按照下面的另一种方法进行
状态变量(Auth0 使用它)- 客户端将生成并随每个请求传递一个加密的强随机随机数,服务器将连同其响应一起回显该随机数,从而允许客户端验证随机数。 Auth0 doc 中对此进行了解释。
使用具有加密强随机值的 CSRF cookie,这样每个 AJAX 请求都会读取 cookie 值并将 cookie 值添加到自定义 HTTP 标头中(GET 和 HEAD 请求除外,它们不应进行任何状态修改)。由于 CSRF 由于同源策略而无法读取任何内容,并且它基于利用 POST、PUT 和 DELETE 等不安全的 HTTP 方法,因此此 CSRF cookie 将减轻 CSRF 风险。所有现代 SPA 框架都使用这种使用 CSRF cookie 的方法。提到了 Angular 方法here
始终检查referer 标头并仅在referer 是受信任域时接受请求。如果没有引用标头或未列入白名单的域,则只需拒绝请求。使用 SSL/TLS 时,通常会出现引用。登陆页面(主要是信息性的,不包含登录表单或任何安全内容可能有点放松,并允许缺少引用标头的请求
应在服务器中阻止 TRACE HTTP 方法,因为这可用于读取 httpOnly cookie
另外,将标头 Strict-Transport-Security: max-age=<expire-time>; includeSubDomains
设置为仅允许安全连接,以防止任何中间人覆盖子域中的 CSRF cookie
【讨论】:
所以:localStorage 与 XSS 的安全问题与其他方法相同,缺点是无法轻松参与 SSO 交换,我读对了吗?只是在某些情况下,为什么我似乎“拒绝”你的其余答案,这不是关于使用现有的提供者,而是关于在我自己的较小应用程序中实现令牌交换。 @Uriziel 从本质上讲,这意味着要使本地存储安全,我们必须使用“指纹”方法(直到“发件人约束访问令牌”方法得到广泛支持),这又需要一个 cookie。因此,就像今天一样,本地存储不能独立运行,这让人们更喜欢 cookie。未来几天情况有望发生变化。但正如你所说,子域限制可能会保留。以上是关于为啥 auth0 建议不要在 localStorage 中存储令牌?的主要内容,如果未能解决你的问题,请参考以下文章
为啥建议不要在 Node.js 代码的任何地方关闭 MongoDB 连接?
为啥建议不要在 Node.js 代码的任何地方关闭 MongoDB 连接?
为啥 Auth0.js 在 Passwordless/Verify 上给我有效负载验证错误“没有满足有效模式”和“缺少必需的属性”?