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