这个答案基于无状态方法,因此它不讨论传统的会话管理
你问了两个完全不同的问题:
- 购物车 - 与业务功能更相关
- OAuth 2 & JWT - 与安全和身份验证相关
作为电子商务网站的用户,我希望我在上班途中从移动设备添加到购物车的任何商品,在我回家后从 PC 登录网站时都应该在购物车中可用。因此,购物车数据应保存在后端数据库中并链接到我的用户帐户。
当涉及到使用 OAuth 2.0 进行身份验证时,JWT 访问令牌和/或刷新令牌需要存储在客户端设备的某个位置,这样一旦用户通过提供登录凭据对自己进行身份验证,他就不需要再次提供他的凭据浏览网站。在这种情况下,浏览器本地存储、会话存储和 cookie 都是有效的选项。但是,请注意,此处的 cookie 未链接到服务器端的任何会话。换句话说,cookie 不存储任何会话 ID。cookie 仅用作访问令牌的存储,访问令牌随每个 http 请求传递到服务器,然后服务器使用数字签名验证令牌以确保它没有被篡改和过期。
尽管访问和/或刷新令牌的所有三个存储选项都很流行,但如果以正确的方式使用,cookie 似乎是最安全的选项。
为了更好地理解这一点,我建议您阅读本文和本文以及 OAuth 2.0 规范。
2019 年 2 月 16 日更新
我之前说过 cookie 似乎是最安全的选项。我想在这里进一步澄清这一点。
我认为浏览器localStorage
并sessionStorage
没有为存储身份验证令牌提供足够的安全性的原因如下:
如果发生 XSS,恶意脚本可以轻松地从那里读取令牌并将它们发送到远程服务器。在那里,远程服务器或攻击者在冒充受害用户方面没有问题。
localStorage
并且sessionStorage
不跨子域共享。因此,如果我们有两个 SPA 在不同的子域上运行,我们将无法获得 SSO 功能,因为组织内的另一个应用程序将无法使用一个应用程序存储的令牌。有一些使用 的解决方案iframe
,但这些看起来更像是解决方法而不是好的解决方案。并且当响应头X-Frame-Options
被用来避免点击劫持攻击时iframe
,任何解决方案iframe
都是不可能的。
但是,可以通过使用指纹(如OWASP JWT 备忘单中所述)来减轻这些风险,而指纹又需要 cookie。
指纹的想法是,生成一个加密的强随机字节串。原始字符串的 Base64 字符串将存储在名称为 prefix的HttpOnly
, Secure
, SameSite
cookie 中__Secure-
。应根据业务需求使用域和路径属性的正确值。字符串的 SHA256 哈希值也将在 JWT 的声明中传递。因此,即使 XSS 攻击将 JWT 访问令牌发送到攻击者控制的远程服务器,它也无法发送 cookie 中的原始字符串,因此服务器可以基于 cookie 的缺失拒绝请求。HttpOnly
XSS 脚本无法读取cookie 。
因此,即使我们使用localStorage
and sessionStorage
,我们也必须使用 cookie 来确保它的安全。最重要的是,我们添加了如上所述的子域限制。
现在,使用 cookie 存储 JWT 的唯一问题是 CSRF 攻击。由于我们使用SameSite
cookie,CSRF 得到缓解,因为跨站点请求(AJAX 或仅通过超链接)是不可能的。如果该网站在任何旧浏览器或其他一些不支持的不太流行的浏览器中使用SameSite
cookie,我们仍然可以通过额外使用具有加密强随机值的 CSRF cookie 来缓解 CSRF,这样每个 AJAX 请求都会读取 cookie 值并将 cookie 值添加到自定义 HTTP 标头中(除了不应该这样做的 GET 和 HEAD 请求)任何状态修改)。由于CSRF由于同源策略无法读取任何内容,并且它基于利用POST,PUT和DELETE等不安全的HTTP方法,因此该CSRF cookie将减轻CSRF风险。所有现代 SPA 框架都使用这种使用 CSRF cookie 的方法。这里提到了Angular 方法。
此外,由于 cookie 是httpOnly
and Secured
,XSS 脚本无法读取它。因此,XSS 也得到了缓解。
还值得一提的是,可以通过使用适当的content-security-policy
响应头进一步减轻 XSS 和脚本注入。
其他CSRF缓解方法
- 状态变量(Auth0 使用它) - 客户端将生成并随每个请求传递一个加密的强随机随机数,服务器将与其响应一起回显,允许客户端验证随机数。它在Auth0 doc 中进行了解释。
- 始终检查引用标头并仅在引用为受信任域时才接受请求。如果引用标头不存在或域未列入白名单,只需拒绝请求即可。使用 SSL/TLS 时,通常会出现引用。登陆页面(主要是信息性的,不包含登录表单或任何安全内容)可能有点放松,并允许缺少引用标头的请求。
- 应在服务器中阻止 TRACE HTTP 方法,因为这可用于读取
httpOnly
cookie。
- 另外,设置标头 Strict-Transport-Security: max-age=; includeSubDomains 只允许安全连接,以防止任何中间人覆盖来自子域的 CSRF cookie。