永不过期的 API 密钥带来的问题
一个多年前签发、从未轮换、至今仍然有效的密钥并不是一种便利。它是一个多年来没有人真正检查过的隐患。
仅与 API 密钥挂钩的速率限制隐含着一个假设:一个密钥对应一个合法用户,并以一种相当可预测的方式使用它。这个假设失效的频率比设计时预想的更高。一个密钥可能在整个团队中共享,可能嵌入在客户端应用中,由许多不同的访客用同一凭据触发请求,也可能被某人专门反复生成多个,以叠加多个密钥来绕过单个密钥的限制。
正是出于这个原因,我们在密钥级别之外还在网络级别进行速率限制。每个网络(IPv4 的一个 /24 地址块或 IPv6 的一个 /48 地址块)每天有 2,500 次请求的免费配额,由无密钥使用和从该网络内地址注册的任何密钥共享。一个人无法从同一个网络生成十个密钥,从而把免费配额扩大十倍,因为无论下面挂着多少个独立的密钥,网络级上限都会捕获这种模式。
这主要不是为了阻止恶意滥用,尽管它也能做到这一点。它是为了让免费配额对所有诚实使用它的人都保持意义。像免费套餐这样的共享资源,只有在其慷慨没有被少数绕过单密钥限制的账户悄悄大量吸走时,才能保持慷慨。网络级配额让免费套餐的账算得更接近它最初的设计本意:按来源提供真实的每日配额,而不是按该来源碰巧持有的凭据数量提供。
有一种正当的使用场景必须小心避免受到惩罚:多个真实用户或服务合法地从同一个网络运行,例如一间办公室、一个共享托管环境,或者一个位于少量公网 IP 地址之后的大型组织。这正是我们公开滚动 IP 名额机制、并通过配额响应头提供网络用量可见性的原因,其中包括一个专门的响应头,显示您的密钥已使用了多少个 IP 名额以及上限是多少。共享同一网络的正当团队可以看到自己的真实状况,而不是在意外触及共享上限时才发现它的存在。
仅在密钥级别进行速率限制,构建和解释起来都更简单,对很多 API 来说可能也已经足够。但对于一个提供可观免费套餐的服务来说,仅按密钥限制会留下一个明显的漏洞:免费配额从来就不是一种可以通过生成更多凭据来成倍放大的资源,而一个没有考虑到这一点的限制,并没有真正保护它声称要保护的东西。在密钥级限制之上叠加一层网络级上限,就能堵上这个漏洞,而无需要求每个正当用户证明自己不是例外。