使用 AWS Cloudfront 缓存查询时如何向 api 添加简单许可

How do I add simple licensing to api when using AWS Cloudfront to cache queries

我在 AWS Elastic Beanstalk 上部署了一个应用程序,我添加了一些简单的许可来阻止滥用 api,用户必须将许可证密钥作为字段

search.myapi.com/?license=4ca53b04&query=fred

如果这无效,则请求被拒绝。

然而,在每月更新之前,上述查询将始终 return 相同的数据,因此我现在将 search.myapi.com 指向 AWS CloudFront 分布,然后只有当查询未被缓存时,它才会作为

转到实际服务器
direct.myapi.com/?license=4ca53b04&query=fred

但是问题是,如果两个用户进行相同的查询,Cloudfront 不会将他们视为相同,因为许可参数不同。所以 Cloudfront 缓存只在每个用户级别上工作,这是没有用的。

我想要做的是让 CloudFront 忽略用于缓存的许可证参数,而不是其他参数。我不太介意这是否意味着用户可以使用无效许可证访问 CloudFront,只要他们无法成功查询服务器(因为 CloudFront 调用很便宜,但服务器调用很昂贵,无论是 cpu 还是货币成本)

也许我需要的是在 CloudFront 前面进行许可证检查然后删除许可证参数的东西,但我不知道那会是什么?

想到了两种可能。

第一个解决方案 感觉像是 hack,但会阻止未经许可的用户成功获取未缓存的查询响应。如果响应被缓存,它会泄漏出去,但在原始服务器资源方面没有成本。

如果内容不敏感,并且您只是想避免琐碎theft/annoyance,这可能是可行的。

对于查询参数,CloudFront 允许您forward all, cache on whitelist

因此,白名单 query(以及任何其他必要的字段)而不是 license

给定查询的结果:

  • 有效许可证,缓存未命中:请求转到源,源returns响应,响应存储在缓存中
  • 有效许可证,缓存命中:缓存提供的响应
  • 无效许可,缓存命中:响应来自缓存
  • 无效许可证,缓存未命中:响应转到原点,原点returns错误,错误存储在缓存中。

糟糕。最后一个条件是有问题的,因为如果进行相同的查询,授权用户将收到缓存的错误。

但我们可以解决这个问题,只要来源 returns 一个无效请求的 HTTP 错误,例如 403 Forbidden.

正如我在 中解释的那样,CloudFront 使用不同的计时器(而不是 min/default/max-ttl)缓存 HTTP 错误响应,默认值为 t 分钟。对于几个单独的 HTTP 状态代码,例如 403,可以将此值设置为 0(或其他值)。因此,对于您的来源的错误代码 returns,将错误缓存最小 TTL 设置为 0 秒。

至此,缓存错误响应并将其回放给授权客户端的问题已经解决。

第二个选项 总体来说似乎是个更好的主意,但需要更复杂的技术并且可能花费更多。

CloudFront 有一项将其与 AWS Lambda 连接起来的功能,称为 Lambda@Edge。这允许您使用简单的 Javascript 脚本分析和操作请求和响应,这些脚本在 CloudFront 信号流的特定触发点 运行。

    在检查缓存之前,每个请求的
  • Viewer Request 运行s。它可以允许请求继续进入 CloudFront,也可以停止处理并生成直接返回给查看器的响应。此处生成的响应不会存储在缓存中。
  • Origin Request 在检查缓存后 运行s,仅针对缓存未命中,在请求转到源之前。如果此触发器生成响应,则将响应存储在缓存中并且不联系源。
  • 原始响应在原始响应到达后 运行 秒,仅用于缓存未命中,并且在响应进入缓存之前。如果此触发器修改了响应,则将修改后的响应存储在缓存中。
  • Viewer Response 运行s 在响应返回给查看器之前,缓存未命中和缓存命中。如果此触发器修改了响应,则不会缓存修改后的响应。

由此,您可以看出这可能有多大用处。

查看器请求触发器可以检查每个请求的有效许可证密钥,并拒绝那些没有的请求。为此,它需要访问一种方法来验证许可证密钥。

如果您的客户群很小或很少改变,则可以将键列表嵌入触发器代码本身。

否则,它需要验证密钥,这可以通过从触发器代码中向源服务器发送请求来完成(运行时间环境允许您的代码发出出站请求并接收响应通过互联网)或通过在托管数据库(如 DynamoDB)中进行查找。

Lambda@Edge 在 Lambda 容器中触发 运行,并且根据流量负载,观察表明到达同一边缘位置的后续请求很可能会由同一容器处理。每个容器一次只处理一个请求,但一旦控制权返回给 CloudFront,容器就可用于下一个请求。因此,您可以将结果缓存在每个容器内的全局数据结构中的内存中,从而显着减少您需要确定许可证密钥是否有效的次数。该函数要么允许 CloudFront 继续正常处理,要么通过生成自己的响应来主动拒绝无效密钥。单个触发器处理的每百万个请求花费不到 1 美元。

此解决方案可防止丢失或未经授权的许可证密钥实际检查缓存或向源发出查询请求。和以前一样,您可能希望在 CloudFront 缓存行为设置中自定义查询字符串白名单以从白名单中消除 license,并更改错误缓存最小 TTL 以确保不缓存错误,即使这些错误永远不会发生。