【问题标题】:How do you secure a RESTful API being consumed by a browser from CSRF attacks?如何保护浏览器使用的 RESTful API 免受 CSRF 攻击?
【发布时间】:2012-01-02 20:09:34
【问题描述】:

我正在为一组网站设计 API。这些站点非常相似(有点像 StackOverflow、SuperUser 和 ServerFault),它们有一个共享的后端是有意义的。因此,我们决定尝试使用一个不错的 REST API 作为后端,并使用一堆非常相似但不同的前端来使用该 API。前端最好都是静态的,但如果结果证明是不可能的,这不是硬性要求。

我现在正在设计该 API,我担心安全隐患,尤其是 CSRF。根据我对 CSRF 攻击的基本理解,它们由两个重要组成部分组成:

  1. 能够命名资源和请求正文。

  2. 诱使用户/浏览器使用环境身份验证(如会话)向看起来经过身份验证的资源发出请求。

许多修复 CSRF 攻击的经典方法都是基于会话的。由于我的 REST API 并没有真正进行会话,这既阻止了很多向量,也阻止了几乎所有修复它们的方法。例如,双重提交没有意义,因为没有什么可以双重提交。

我最初的方法涉及攻击 CSRF 攻击的第 2 部分。如果我对所有请求进行身份验证(例如使用 HTTP Basic Auth),并且浏览器不保存这些凭据(例如,某些 JS 发出请求),那么只有具有凭据的 JS 才能发出请求,我们就完成了.明显的缺点是应用程序需要知道用户的凭据。另一个不太明显的缺点是,如果我想在 API 端安全地存储凭据,那么验证密码应该花费固定的、重要的时间。如果安全地验证密码需要 100 毫秒,那么每个其他请求将至少需要 100 毫秒 + eps,并且需要一些非常聪明的客户端技巧才能让这感觉不慢。我也许可以缓存它(因为凭据将始终相同),如果我非常小心,我可能会设法做到这一点而不会引入计时漏洞,但这听起来像马蜂窝。

OAuth 2.0 似乎有点过头了,但我想它毕竟可能是最好的解决方案,以免我最终实施得很糟糕。我想我现在可以做 HTTP Basic Auth 的事情,当我们有第三方应用程序开发人员时,我可以转向 OAuth。

与 OAuth 存在一些阻抗不匹配。基本上,OAuth 真的想帮助应用程序访问另一个应用程序上的东西。我希望用户在这样的帐户存在之前注册其中一个前端。

我还考虑了通过使 URL 随机化来攻击第 1 点——即将标记添加到查询字符串。这肯定会奏效,并且非常接近表单中传统随机令牌的工作方式,并且考虑到 HATEOAS,它甚至应该是相当 RESTful 的,尽管这引发了两个问题:1)你从哪里开始?是否有一个强制性的 API 起点,您可以使用 HTTP Basic Auth 登录? 2) 如果应用开发者不能预先预测一个 URL,那该死的 HATEOAS 会让他们高兴吗?

我见过How to prevent CSRF in a RESTful application?,但我不同意随机 URI 必然是非 RESTful 的前提。此外,这个问题并没有真正令人满意的答案,也没有提到 OAuth。此外,正如我上面提到的(静态前端的域与 API 端点的域不同),会话双重提交解决方案无效。

我意识到我在这里所做的基本上是尝试允许来自一个域的跨站点请求并禁止来自另一个域的跨站点请求,这并不容易。肯定有一些合理的解决方案?

【问题讨论】:

  • 如您所说,问题在于您既要阻止跨站点请求又要允许跨站点请求。问薛定谔。
  • 这并不意味着没有办法解决问题。我什至提供了一个,虽然有点慢。

标签: javascript security rest


【解决方案1】:

根据定义,CSRF 令牌是“每个用户状态”,因此不是 RESTful。为了安全起见,大多数 API 都打破了“每个用户状态”的要求,并要求将 CSRF 令牌作为 HTTP 标头参数传递。

preventing CSRF还有其他方式。检查引用者不如 CSRF 令牌强,但它是 RESTful 并且非常不太可能被破坏。请记住,缺少引用者应被视为失败的请求。

XSS 可用于绕过基于令牌的 CSRF 预防和基于引用者的 CSRF 预防。

【讨论】:

  • 与我对您的评论的回复中的评论相同。您能否详细说明为什么在使用 JS 添加身份验证标头时 HTTP 基本身份验证不会阻止 CSRF?另外,OAuth 如何不阻止 CSRF? OAuth 的一次性令牌部分不能保证这一点吗?
  • @lvh 用户名和密码是与请求一起发送的每个用户的状态。用作 CSRF 令牌的 Cryptographic Nonce 也是随每个请求发送的每个用户状态,但它更安全,因为它应该是与会话绑定的动态值。通常 RESTful 协议依赖于静态加密 nocne 或“api 密钥”,它是每个用户的状态并且也停止 CSRF。在 HTTP 标头中发送它并不会神奇地让它变得安静,但如果该浏览器发送的其他请求包含此值,它可以打开攻击之门。
  • 但这就是我的意思。如果它没有成为环境浏览器身份验证,攻击向量是什么?对于 CSRF 而言,无论是 nonce 还是实际凭证都无关紧要:浏览器不知道,那么它怎么能被欺骗提供呢?
  • @ivh,真的需要这么说吗?只要证明你的假设是正确的。
  • 在 httponly cookie 中的 csrf 令牌可以防止 XSS 攻击
【解决方案2】:

答案取决于您需要支持什么。如果我们假设您希望支持一个使用 REST 服务的 Web 应用程序,并为一个与恰好是 RESTFUL 的 Web 应用程序不同的 API 使用相同的 REST 服务(您可以决定“会话”适合您!)。

现在(/me 很累)我认为您建议使用带有 HTTP Basic Auth 的 javascript 的方式是一个好的开始。

【讨论】:

  • HTTP Basic Auth 易受 CSRF 攻击。
  • 您能详细说明一下吗?我意识到浏览器会缓存 HTTP Basic Auth,但我们并没有要求浏览器这样做;我们建议让一些 JS 构建 HTTP Basic Auth 标头。因此,凭据永远不会出现在浏览器的环境身份验证池中,并且系统成为 CSRF 免疫的。对吗?
【解决方案3】:

我有另一个潜在的解决方案,所以我将其列为答案。请随意拆开它:)

auth.platform.com 接受身份验证并设置 cookie。如果auth.site.comauth.platform.com 的CNAME,那么对auth.site.com 的请求(解析后以auth.platform.com 结束)是否能够为site.com 设置cookie?这样我就可以重复提交会话 cookie。

当然,auth.platform.com 只会为少数列入白名单的域设置 cookie。

编辑:当然这根本行不通,因为你必须使用 HTTPS 来安全地进行身份验证,而 HTTPS 会看穿你的诡计。

【讨论】:

    【解决方案4】:

    将您的 API 设计为真正的 RESTful API,可以防止最常见的 CSRF 向量:

    • 避免使用 cookie 可以防止它们被“窃取”,
    • 将 GET 用于“安全”操作可防止 imgiframes 在没有用户交互的情况下触发不安全操作。

    那么你应该实现CORS 让用户的浏览器阻止来自你不信任的来源的请求。

    【讨论】:

    • -1 这些都与 CSRF 无关,除了关于 CORS 的评论。 CORS 很危险,可用于读取 CSRF 令牌或其他敏感信息。 CORS 不能用于防止 CSRF。
    猜你喜欢
    • 2015-12-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-10-02
    • 2019-09-08
    • 1970-01-01
    • 2017-12-04
    相关资源
    最近更新 更多