Dogfooding我们自己的速率限制API

时间:2016-01-22 04:49:56

标签: javascript api rest

概述:

我公司开发了一种限速API。我们的目标是双重的:

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

澄清:为什么要限制价格?

我们限制我们的API,因为我们将其作为我们产品的补充销售。对我们的API的匿名访问每小时API调用的门槛非常低,而我们的付费客户每小时或更多时间允许超过1000次调用。

问题:

我们的速率限制API非常适合开发人员生态系统,但为了让我们能够食用它,我们不能将其限制在相同的速率限制范围内。我们API的前端是所有JavaScript,直接对API进行Ajax调用。

所以问题是:

  

如何保护api,以便在删除速率限制的过程中可以消除速率限制?

探索解决方案(以及它们无效的原因)

  1. 根据主机标头验证引荐来源。 - 有缺陷,因为推荐人很容易被伪造。

  2. 使用HMAC根据请求和共享密钥创建签名,然后在服务器上验证请求。 - 有缺陷,因为通过查看前端JavaScript可以轻松确定秘密和算法。

  3. 代理请求并在代理中签署请求 - 仍然存在缺陷,因为代理本身会公开API。

  4. 问题:

    我期待着Stack Overflow上的精彩人才提出替代解决方案。你会如何解决这个问题?

9 个答案:

答案 0 :(得分:93)

由于您自己的JavaScript客户端直接访问API,因此任何人都可以查看它正在做的事情并模仿它,包括使用相同的API密钥。您可以尝试使其变得更加困难,例如通过混淆代码或在路上设置各种障碍,但您和您试图约束的人基本上具有相同的访问权限。您不需要尝试创建特权差异,而是需要构建一个系统,非官方客户端使用其范围内的所有访问权限,但系统的排列方式是:所有客户的官方使用都更大。

这通常使用每用户访问令牌,而不是整个应用程序的一个令牌。每个令牌的限制对于API的典型使用应该是充足的,但对于试图滥用它的人来说则是限制性的。例如,每分钟100次通话可能足以支持典型的浏览,但如果我想抓住你,我就无法在预算范围内有效地进行。

总会有军备竞赛 - 我可以通过创建大量的僵尸用户帐户来绕过极限。但是,如果你只是在你的注册流程中添加一个验证码,那么这是一个非常好的解决问题,只需花费一小部分费用给真正的人类。当你进入这些场景时,一切都只是在便利和限制之间进行权衡。你永远不会找到完全防弹的东西,所以要专注于让它变得足够好并等到有人利用你去了解洞的位置。

答案 1 :(得分:33)

如果这导致您出现问题,则会导致您假定的开发人员生态系统出现问题(例如,当他们尝试开发替代用户界面时)。如果你真的在吃自己的狗粮,那么API(以及速率限制)适合你的应用。以下是一些建议:

  • 不要按IP地址限制费率。相反,通过与用户相关联的东西来限制速率,例如,他们的用户ID。在身份验证阶段应用速率限制。

  • 设计您的API,以便用户无需连续调用它(例如,提供一个返回许多结果的列表调用,而不是每次都返回一个项目的重复调用)

  • 使用您期望开发人员生态系统具有的相同约束来设计您的Web应用程序,即确保您可以在合理的限制率内进行设计。

  • 确保您的后端具有可扩展性(横向优先),因此您无需在如此低的级别强加限制,实际上会导致UI出现问题。

  • 确保您的节流能够应对爆发,并限制长期滥用。

  • 确保您的限制针对您要删除的滥用情况执行合理的操作。例如,考虑排队或推迟轻度滥用者而不是拒绝连接。大多数Web前端只会同时打开四个同时连接。如果您延迟尝试打开第五个,您只会遇到他们与Web客户端(两个Web客户端)同时使用CLI的情况。如果你延迟第n个API调用没有间隙而不是失败,最终用户会看到事情变慢而不是中断。如果将此结果与仅一次排队的N API调用结合使用,您只会遇到并行处理大量API调用的人,这可能不是您想要的行为 - 例如100个同步的API调用然后一小时的差距通常比一小时内100个连续的API调用差得多。

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

答案 2 :(得分:11)

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

"对我们的API的匿名访问每小时API调用的门槛非常低,而我们的付费客户每小时或更长时间允许超过1000次调用。"

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

答案 3 :(得分:9)

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

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

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

答案 4 :(得分:8)

假设有问题的应用程序必须公开,您没有太多选择:

选择另一种方式来展示API的强大功能。例如,编写此类应用并共享其来源,但实际上并未运行该代码。确保它已被充分记录,以便任何人都可以部署它并看到它正常工作(受到限制)。

您运行的应用程序需要进行重构,以避免客户端API请求并提供更多服务器呈现。你仍然可以熟悉你的API,但不是以明显的方式 - 从服务器端向无限制的API提出安全请求。

调整速率限制以允许您的应用运行并投资于性能优化以处理负载。

是的,首先让核心API无油门,并将其保存在专用网络中。在单独的可公开访问的层中进行节流。

答案 5 :(得分:4)

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

例如,如果您需要在实例之间共享数据,请将整个部署在公司防火墙后面,并将应用程序附加到与面向公众的实例相同的数据库中。

答案 6 :(得分:4)

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

不能简单地复制ID以进行欺骗,因为它仅对单个IP地址,用户和有限的时间有效。因此,攻击者必须调用您的页面并从JavaScript源中过滤掉密钥,或者在每次新用户想要使用它时拦截Ajax请求。

另一种选择:

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

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

因此,其他人不会将您的代理用于他们自己的应用程序,您每天都会更改混淆方案。模糊的调用名称可以在JavaScript源代码中自动生成,并在代理中进行配置。

希望使用此功能的客户还需要跟上不断变化的混淆以使用您的代理。您仍然可以使用referrer-headers和类似的日志记录,因此您可以找到使用您的代理的人。或者在更改混淆方案时捕获它们。

答案 7 :(得分:3)

  • 白名单源IP地址
  • 使用VPN,白名单VPN成员
  • 如果您可以保护代理并且不关心嗅探流量的MITM攻击,那么添加HTTP标头的代理解决方案或浏览器插件应该没问题
  • 任何涉及秘密的解决方案都可以通过每天轮换秘密来减轻泄漏的影响

答案 8 :(得分:2)

设置多个帐户,并在每次请求时随机选择其中一个帐户,或者更改每小时左右使用的帐户。这样,您就可以通过n帐户分配负载,最多可以提高n倍的限制。

如果您试图找到其他用户这样做,如果客户不允许,请小心不小心关闭自己。