Dogfooding我们自己的限速API
概述:
我的公司开发了一个限速API。 我们的目标是双重的:
- 答:围绕我们的产品创build一个强大的开发者生态系
- B:通过使用它来驱动我们自己的应用程序来演示我们的API的强大function。
澄清:为什么限制费率?
我们评估我们的API的限制,因为我们把它作为我们的产品的一个补充。 匿名访问我们的API对于每小时API调用的阈值是非常低的,而我们的付费客户每小时允许超过1000个电话或更多。
问题:
我们的限速API对于开发者生态系统来说是非常好的,但是为了让我们能够减肥,我们不能把它限制在相同的速率限制之内。 我们的API的前端是所有JavaScript,直接对API进行Ajax调用。
所以问题是:
你如何确保api的速度限制可以消除,在这个过程中去除这样的速度限制不容易被欺骗?
探索解决scheme(以及为什么他们不工作)
-
根据主机头确认引用者。 – 缺陷,因为推荐人很容易被伪造。
-
使用HMAC根据请求和共享密钥创build签名,然后在服务器上validation请求。 – 有缺陷,因为秘密和algorithm很容易通过查看前端JavaScript来确定。
-
代理请求并在代理中签署请求 – 仍有缺陷,因为代理本身暴露了API。
问题:
我正在寻找堆栈溢出的杰出思想来提出备用解决scheme。 你将如何解决这个问题?
由于你自己的JavaScript客户端直接访问API,所以任何人都可以看看它在做什么,并模仿它,包括使用相同的API密钥。 你可以试着让它变得更加困难,比如通过混淆你的代码或者阻碍各种障碍,但是你和那些你想要约束的人基本上有相同的访问权限。 与其试图在特权上创造差异,不如build立一个非官方的客户端使用其范围内的所有访问权限的系统,但系统的安排方式使得所有客户端的官方使用是更大。
这通常是针对每个用户的访问令牌来完成的,而不是针对整个应用程序的一个令牌。 每个令牌的限制应该足够用于您的API的典型用途,但是限制尝试滥用它的人。 例如,每分钟拨打100个电话可能足以支持典型的浏览,但是如果我想刮掉你,那么在这个预算上我就无法有效地做到这一点。
总会有一场军备竞赛 – 我可以通过创build大量的bot用户帐号来避开这个限制。 不过,如果您只是在注册stream程中添加一个validation码,那么对于真正的人而言,这是一个相当成功的解决scheme。 当你进入这些情景时,一切都只是便利和限制之间的折衷。 你永远找不到完全防弹的东西,所以要把它做得足够好,等到有人利用你去了解这些洞是什么。
如果这导致你的问题,它会导致你的假定生态系统的开发者一个问题(例如,当他们尝试开发一个替代的用户界面)。 如果你真的在吃自己的狗粮,那就把API(和速率限制)用于你的应用程序。 这里有一些build议:
-
不要按IP地址进行速率限制。 而是通过与用户关联的东西(例如他们的用户ID)进行速率限制。 在authentication阶段应用速率限制。
-
devise您的API,以便用户不需要连续调用它(例如,给出一个返回许多结果的列表调用,而不是每次返回一个项目的重复调用)
-
devise您的Web应用程序时,您希望开发人员生态系统具有相同的约束条件,即确保您可以在合理的限制速率内进行devise。
-
确保你的后端是可扩展的(水平优先),所以你不需要在这么低的水平施加限制,实际上导致了一个用户界面的问题。
-
确保你的节stream有能力应付突发,并限制长期滥用。
-
确保您的限制执行适合您正在寻求删除的滥用行为的合理行动。 例如,考虑排队或延迟轻度滥用者,而不是拒绝连接。 大多数networking前端只能同时打开四个同时连接。 如果你延迟尝试打开第五个,那么你将只能在Web客户端(或两个Web客户端)同时使用CLI的情况下。 如果您拖延第n个API调用而没有发生缺陷,最终用户会看到速度减慢而不是中断。 如果您将这一function与仅一次排队的N API调用结合使用,那么您只会触及并行大量API调用的人员,这可能不是您想要的行为 – 例如,100个同步API调用,那么一个小时的差距通常是很远的在一个小时内连续超过100次API调用。
这不是回答你的问题吗? 那么,如果您确实需要按照您的要求进行操作,请在身份validation阶段进行费率限制,并根据您的用户所在群组应用不同的费率限制。 如果您使用的是一组凭据(由您的开发人员和QA团队使用),则会获得更高的费率限制。 但是,您可以立即明白为什么这将不可避免地导致您的生态系统发现您的开发人员和QA团队看不到的问题。
购买你的产品。 成为你自己的付费客户。
“匿名访问我们的API对于每小时API调用的门槛非常低,而我们的付费客户每小时允许超过1000个电话或更多。”
这也有助于从客户的angular度来testing系统。
不幸的是,这没有完美的解决scheme。
一般的方法通常是为客户提供一种可欺骗的方式来识别他们自己(例如一个标识符,版本和API密钥),以便客户注册可用于限制访问的信息(例如客户端一个服务器在一个给定的IP地址范围内,所以只允许在这个范围内的调用者;例如,客户端是JavaScript,但是只传递给特定类别的浏览器,所以只允许访问指定特定用户代理string的HTTP请求;等等) ,然后使用机器学习/模式识别来检测可能是欺骗客户端的exception使用情况,然后拒绝来自这些欺骗客户端的stream量(或向客户端确认这些使用情况确实不是来自合法客户端,而是replace他们的可欺骗凭证,然后使用较旧的欺骗性证书禁止进一步的stream量)。
通过使用多层密钥,可以使其更难以欺骗。 例如,您发出一个长寿命的证书(它只能在有限的一组IP地址范围内使用)进行API调用,以logging关于客户端(例如用户代理)的信息返回在JavaScript中联合使用的较短寿命的客户端密钥,以用于客户端上的客户端API请求。 这也是不完美的(一个欺骗者可以发出相同的服务器调用来获得证书),但是如果返回的API密钥包含在混淆的(并且频繁变化的)JavaScript或者HTML中(这会使得它变得困难从响应中可靠地提取)。 这也提供了一种更容易检测欺骗的方法; 客户端密钥现在绑定到特定的客户端(例如,特定的用户代理,甚至特定的cookie jar),使得在另一个客户端中的重用容易被检测,并且到期还限制了欺骗密钥可以被重用的持续时间。
假设有问题的应用程序必须公开,您没有太多的select:
select另一种方式来展示您的API的力量。 例如,编写这样一个应用程序并分享它的源代码,但不要真正运行该代码。 确保它有很好的logging,以便任何人都可以部署它,并看到它工作(受限制)。
您运行的应用程序需要进行重构,以避免客户端API请求和更多的服务器呈现。 你仍然可以使用你的API,但不是以一种明显的方式 – 从服务器端安全地请求节streamAPI。
调整速率限制以允许您的应用程序工作并投入性能优化来处理负载。
是的,首先将核心API节stream,并将其保存在专用networking中。 在独立的公共访问层中进行节stream。
你能站起来一个单独的UI和节streamAPI的实例,然后限制访问来自你的组织的IP地址吗?
例如,如果需要在实例之间共享数据,则将整个事件部署在企业防火墙后面,并将该应用程序附加到与面向公共的实例相同的数据库。
您可以尝试生成一个唯一的会话ID,绑定到特定的IP地址/用户和有限的生存时间。 当用户下载您的应用程序前端时,JavaScript代码将生成的会话ID注入到JavaScript源代码中。 会话ID将附加到您的API的每个请求,并解除费率限制。
该ID不能简单地复制为欺骗,因为它只对一个IP地址,用户和有限的时间有效。 所以攻击者必须调用你的页面,并从JavaScript源中过滤掉密钥,或者在每次新用户想要使用Ajax请求时拦截Ajax请求。
另外一个select:
为您自己的应用程序设置一个代理并使用混淆。 对代理的Ajax请求使用来自真实API调用的不同名称,并由代理将其翻译回来。 所以你的应用程序不会在你的真实API上调用getDocument
,但它会在你的代理上调用getFELSUFDSKJE
。 代理将把这个callback转换成getDocument并转发给实际的速率限制的API。
您的实际API不会限制代理的请求。
而其他人不会将自己的代理用于自己的应用程序,而是每天更改混淆模式。 模糊的呼叫名称可以在您的JavaScript源代码中自动生成,并在代理中configuration。
希望使用此function的客户也需要跟上不断变化的混淆,以使用您的代理。 而且你仍然可以使用引用标头和类似的日志logging,所以你可以find使用你的代理人。 或者在更改混淆模式时抓住它们。
- 白名单来源IP地址
- 使用VPN ,白名单VPN成员
- 代理解决scheme或添加HTTP标头的浏览器插件应该没问题,如果您可以保护代理并不关心MITM攻击嗅探stream量
- 任何涉及秘密的解决scheme都可以通过每天旋转秘密来减轻泄漏的影响
设置多个帐户,并随机select其中的一个请求,或更改每个小时左右使用哪一个。 通过这种方式,您可以将负载分配给n
帐户,从而使您获得高出n
倍的限制。
如果你试图find其他用户这样做,如果不允许客户,请小心意外closures自己。