狗粮我们自己的速率有限的 API

概述:

My company has developed a rate-limited API. Our goal is twofold:

  • 答: 围绕我们的产品创建一个强大的开发者生态系统。
  • B: 通过使用它来驱动我们自己的应用程序来展示我们的 API 的威力。

澄清: 为什么要设定利率上限?

We rate limit our API, because we sell it as an addition to our product. Anonymous access to our API has a very low threshold for API calls per hour, whereas our paid customers are permitted upwards of 1000 calls per hour or more.

The Problem:

我们的速率限制 API 是伟大的开发者生态系统,但为了我们的狗粮,我们不能允许它被限制在同样的速率限制。我们的 API 的前端都是 JavaScript,对 API 进行直接的 Ajax 调用。

所以问题是:

你如何保护一个 api,使利率限制可以删除的地方,在消除这种利率限制的过程中不容易欺骗?

探索解决方案(以及为什么它们不起作用)

  1. Verify the referrer against the host header. -- 有缺陷是因为 < strong > 参考者 很容易被伪造。

  2. 使用 HMAC根据请求和共享秘密创建签名,然后在服务器上验证请求。—— Flawed because the secret and algorithm would be easily determined by looking into the front end JavaScript.

  3. 代理请求并在代理中签名请求—— 仍然存在缺陷,因为代理本身暴露了 API。

The Question:

我期待着对堆栈溢出的聪明头脑提出替代解决方案。你将如何解决这个问题?

7255 次浏览

不幸的是,没有完美的解决方案。

通常的方法是为客户端提供一种 可笑方式来识别他们自己(例如,一个标识符,版本和 API 密钥——例如) ,为客户端注册关于他们自己的信息,可以用来限制访问(例如,客户端是一个给定的 IP 地址范围内的服务器,因此只允许在该范围内的调用者; 例如,客户端是 JavaScript,但只传递到一个特定类别的浏览器,因此只允许访问指定特定用户代理字符串的 HTTP 请求等) ,然后使用机器学习/模式识别来检测可能是欺骗客户端的异常使用,然后拒(或者向客户机确认这些用法确实不是来自合法的客户机,替换它们的欺骗凭证,然后禁止使用旧的欺骗凭证进一步传输).

通过使用多层密钥,可以稍微增加欺骗的难度。例如,您发出一个存在于服务器上的长期凭证(并且只能在有限的 IP 地址范围内使用) ,以进行一个 API 调用,该调用记录有关客户端的信息(例如用户代理) ,并返回一个短期的客户端密钥,该密钥在 JavaScript 中聚合,用于客户端 API 请求。这也是不完美的(欺骗程序可以发出相同的服务器调用来获得凭证) ,但是如果返回的 API 密钥包含在模糊的(经常变化的) JavaScript 或 HTML 中(这将使得从响应中可靠地提取变得困难) ,那么这将更加困难。这也提供了一种更容易检测欺骗的方法; 客户端密钥现在绑定到一个特定的客户端(例如,特定的用户代理,甚至可能是一个特定的 cookie jar) ,这使得在另一个客户端的重用更容易检测,过期也限制了欺骗密钥可能被重用的持续时间。

因为您自己的 JavaScript 客户端是直接访问 API 的,所以任何人都可以查看它在做什么并模拟它,包括使用相同的 API 键。您可以尝试让它变得更加困难,比如混淆代码或者设置各种障碍,但是您和您试图约束的人拥有基本相同的访问权限。您需要构建一个系统,在这个系统中,非官方客户机使用其作用域内的所有访问权限是完全可以的,但是系统的安排方式使得所有客户机的官方使用更多。

这通常是使用每个用户的访问令牌来完成的,而不是使用整个应用程序的一个令牌。对于 API 的典型使用,每个令牌的限制应该足够多,但是对于试图滥用它的人来说是有限制的。例如,每分钟100个电话可能足够支持典型的浏览,但如果我想刮你,我不能有效地做到这一点的预算。

总会有一场军备竞赛-我可以通过创建大量的机器人用户帐户来绕过这个限制。不过,如果你只是在注册流程中添加一个验证码(captcha) ,那么这个问题就已经相当解决了,只需要真正的人付出一点点代价。当您进入这些场景时,一切都只是方便和限制之间的权衡。你永远不会找到完全防弹的东西,所以专注于让它足够好,并等待,直到有人利用你,以了解在哪里的漏洞。

你能够建立一个单独的 UI 和无节流 API 实例,然后限制对来自你的组织的 IP 地址的访问吗?

例如,将整个系统部署在公司防火墙之后,如果需要在实例之间共享数据,则将应用程序与面向公众的实例连接到同一个数据库。

  • 白名单源 IP 地址
  • 使用 VPN、白名单 VPN 成员
  • 代理解决方案或浏览器插件,添加 HTTP 头应罚款,如果您可以保护代理,并不担心 麻省理工学院攻击嗅探的流量
  • 任何涉及机密的解决方案都可以通过每天更换机密来减轻泄密的影响

假设有问题的应用程序必须公开开放,你别无选择:

选择另一种方式来演示 API 的强大功能。例如,编写这样一个应用程序并共享它的源代码,但是不要实际运行这些代码。但是要确保它有良好的文档记录,这样任何人都可以部署它并看到它的工作情况(受节流的影响)。

您运行的应用程序需要重构,以避免客户端 API 请求,并更多地呈现服务器。您仍然可以对您的 API 进行添加,但不是以一种显而易见的方式ーー从服务器端发出无节流 API 的安全请求。

Adjust rate limitation to allow for your app to work and invest into performance optimization to handle the load.

是的,首先要让核心 API 没有节流阀,并将其保存在一个私有网络中。在一个单独的公共可访问层中的节流。

如果这给您带来了问题,那么它将给您假定的开发人员生态系统带来问题(例如,当他们试图开发替代 UI 时)。如果您真的在吃自己的狗粮,那么让 API (和速率限制)为您的应用程序工作。以下是一些建议:

  • Do not rate limit by IP address. Rather, rate limit by something associated with the user, e.g. their user ID. Apply the rate limit at the authentication stage.

  • 设计你的 API,让用户不需要连续调用它(例如,给出一个返回多个结果的列表调用,而不是每次返回一个条目的重复调用)

  • 设计你的 web 应用程序时,要考虑到你所期望的开发者生态系统的约束条件,也就是说,要确保你可以在合理的节流速度内设计它。

  • Ensure your back end is scalable (horizontally preferably) so you don't need to impose throttling at levels so low it actually causes a problem to a UI.

  • 确保你的节流有能力应付爆发,以及限制长期滥用的能力。

  • 确保你的节流执行合理的行动,适合滥用你正在寻求消除。例如,考虑排队或延迟轻微滥用者,而不是拒绝连接。大多数网络前端一次只能打开四个同时连接。如果你推迟打开第五个的尝试,你只会碰到这种情况,他们正在使用一个 CLI 同时作为网络客户端(不是两个网络客户端)。如果在没有间隔的情况下延迟 n-th API 调用,而不是使其失败,那么最终用户将看到事情变慢而不是中断。如果你一次只对 N 个 API 调用进行排队,你只会碰到那些并行处理大量 API 调用的人,这可能不是你想要的行为——例如100个同时的 API 调用,那么一个小时的间隔通常比一个小时内100个连续的 API 调用要糟糕得多。

这还不能回答你的问题吗?好吧,如果你真的 需要做你所要求的,速率限制在认证阶段,并应用一个不同的速率限制基于组你的用户适合。如果您使用一组凭据(由开发人员和 QA 团队使用) ,那么您将获得更高的速率限制。但是您可以立即看到为什么这将不可避免地导致您的生态系统看到您的开发人员和 QA 团队没有看到的问题。

您可以尝试生成一个惟一的会话 ID,绑定到某个 IP 地址/用户,并限制生存时间。当用户下载您的应用程序前端 JavaScript 代码时,将生成的会话 ID 注入到 JavaScript 源代码中。会话 ID 将被附加到每个请求到您的 API 和速率限制被取消。

The ID cannot be simply copied for spoofing, because it is only valid for a single IP address, user and limited amount of time. So an adversary would have to call your page and filter out the key from your JavaScript source or from intercepting the Ajax request every time a new user wants to use it.

另一个选择:

为您自己的应用程序设置代理并使用模糊处理。对代理的 Ajax 请求使用与实际 API 调用不同的名称,代理将这些名称转换回来。因此,应用程序不会在实际的 API 上调用 getDocument,但会在代理上调用 getFELSUFDSKJE。代理将把这个调用转换回 getDocument,并将其转发到实际的速率有限的 API。

您的实际 API 不会限制代理的请求速率。

因此,其他人不使用您的代理为他们自己的应用程序,您每天更改模糊处理方案。模糊的调用名称可以在 JavaScript 源代码中自动生成,并在代理中配置。

希望使用此代理的客户端,也需要跟上您不断变化的混淆来使用您的代理。并且您仍然可以使用引用标头和类似的方式进行日志记录,这样您就可以找到使用您的代理的用户。或者在改变混淆方案时抓住他们。

设置多个帐户,每次请求时随机选择一个帐户,或者每小时左右更改一次使用哪个帐户。通过这种方式,您可以将负载分布在 n帐户上,使您的负载上限达到 n的两倍。

如果您试图寻找其他用户这样做,如果客户不允许这样做,那么要小心意外地关闭自己。

购买你的产品,成为你自己的付费客户。

“对我们 API 的匿名访问每小时的 API 调用门槛非常低,而我们的付费客户每小时可以接到1000个以上的 API 调用。”

这也有助于从客户的角度测试系统。