如何在单页应用中处理 CSRF 保护?

信息安全 Web应用程序 csrf
2021-08-28 00:37:06

我目前正在构建一个带有 JavaScript/HTML 前端的单页应用程序。前端调用用 .NET 编写的 WEB API。目前我有一个 HTML 页面,用户在其中输入他们的凭据并单击登录。然后将 AJAX 请求发送到 WEB API,该 API 对用户进行身份验证并返回包含 Json Web 令牌的 HTTP-Only cookie。然后浏览器在每个后续请求中发送此 cookie,控制器对其进行验证。

以上运行良好,但缺少 CSRF 保护。我试图找出实现这一点的最佳方法。根据我的研究,看起来有几个选择。

  1. https://www.jamesward.com/2013/05/13/securing-single-page-apps-and-rest-services上的文章建议从 cookie 中提取令牌,然后将其作为请求标头提交。为此,我需要删除 HTTP-Only 标志,以便 JavaScript 可以访问 cookie。我认为这不是最好的解决方案,因为如果发现 XSS 漏洞,它可能会暴露会话信息。有什么我想念的吗?

  2. 我在其他地方读到过发送一个包含 CSRF 令牌的单独 cookie 并使用 JavaScript 从中提取值。然后在后续的 AJAX 请求中将该值作为自定义标头发送,并让 WEB API 基于该标头进行验证。我为此编写了一个快速的概念验证应用程序,并发现浏览器仍会在未来的请求中发送 CSRF 令牌所传递的 cookie。这会造成问题吗?带有 CSRF 攻击的页面能否以某种方式利用这一点?

  3. 与上面类似,但不是通过 cookie 传递 CSRF 令牌,而是通过自定义标头传递它,并让 JavaScript 从那里读取它。这似乎是最直接的方法,但是您看到这种方法有什么缺点吗?

此外,在用户的整个会话中继续使用单个 CSRF 令牌是否安全,或者是否应该在每次请求时刷新它?

2个回答

您可以使用经典的 CSRF 令牌策略,但在基于 AJAX 的应用程序中使用它们可能需要一些麻烦,并且更简单的选项可用于 AJAX 特定的端点:

  • 向服务器的请求添加额外的标头,例如“X-Requested-With: XMLHttpRequest”或“X-Is-Local-XHR: true”,并使您的服务器在经过身份验证的请求上需要标头。使用旧版 Flash 的用户可能容易受到攻击(尽管旧版 Flash 也有更严重的漏洞,所以您可以选择这对您是否重要)。请参阅https://stackoverflow.com/questions/17478731/whats-the-point-of-the-x-requested-with-header

  • 验证是否设置了 Origin 或 Referer 标头并与 Host 标头中的域匹配。Chrome 和 Firefox 为所有 POST 请求发送 Origin 标头,但旧版浏览器可能不包含它。出于隐私原因,某些用户禁用了 Referer 标头。任何一个具有有效值的存在都足以验证请求。

这是一个老话题,有很多零散的讨论(包括在 Security.SE 上)。然而,我建议你完全取消 cookie,而不是模拟基于 cookie 和 post-back 的 CSRF 保护方案的常见建议。

将您的单页应用程序视为真正的应用程序,并使其使用标准方法向 Web API 进行身份验证。例如,您可以使用 OAuth 令牌和无会话请求。(如果 OAuth 太难设置,可以使用自定义令牌方案。)

这也解决了 CORS 的问题。