您如何防止用户通过按住刷新键来关闭您的应用程序?

How do you prevent a user from taking down your application by holding down the refresh key?

我的 Nginx/Rails 应用程序服务器最近出现故障。事实证明,我们受到了对特定 URL 的请求的轰炸,需要几秒钟才能加载。似乎用户在几分钟内不断刷新该页面 - 我的猜测是他们不小心在键盘上放置了一些 object,从而触发浏览器不断刷新。

不管是什么原因,我都需要针对此类问题采取保护措施,并注意这不是静态内容 - 它是动态的,user-specific 内容位于身份验证之后。

我研究过使用 Cache-Control 但这似乎是 non-starter - 至少在 Chrome 上,刷新同一选项卡中的页面将触发请求,无论Cache-Control header (cf iis - Is Chrome ignoring Cache-Control: max-age? - Stack Overflow)

我相信答案可能是速率限制。如果是这样,我将无法基于 IP 来完成,因为我们的许多客户共享同一个 IP。但是我可以添加一个新的 header 来识别用户,然后基于此在 Nginx 中应用速率限制。

这听起来像是前进的方向吗?感觉应该是比较普遍的问题吧!

如果需要立即缓解,

Nginx rate limiting 是一种快速配置更新。正如其他人所提到的,与此结合使用缓存也是理想的选择。

server {
  # DoS Mitigation - Use IP and User Agent to prevent against NAT funnels from different computers
  limit_req_zone $host$binary_remote_addr$http_user_agent zone=rails_per_sec:10m rate=2r/s;

  upstream rails {...}

  try_files $uri $uri/ @rails;

  location @rails {
    limit_req zone=rails_per_sec burst=10 nodelay;
    ...
  }
}

$http_authorization header 或唯一的 cookie(例如 $cookie_foo)也可用于唯一标识会与相同 IP/user-agent 值冲突的请求。

limit_req_zone $host$binary_remote_addr$http_authorization  ...;
limit_req_zone $host$binary_remote_addr$cookie_foo          ...;

我的一位同事提出了一个我认为最适合我们情况的解决方案。我会解释为什么这对其他人有用。

请注意,我们接收请求的速度很低 - 每秒仅 6 个。这是一个问题的原因是有问题的页面是一个加载速度非常慢的报告,只有经过身份验证的用户才能访问。

Server-side 缓存对我们来说不是一个很好的解决方案,因为它需要在每个受影响的页面上单独实施,而且我们有一个包含许多不同控制器的复杂应用程序。

Rate-limiting 通过 Nginx 可能是可行的,但很难优化,而且还存在可测试性问题。

无论如何,我同事的解决方案如下:我们已经有一个 table 记录每个请求的详细信息,包括发出请求的用户的 ID。为了查明用户是否刷新过于频繁,我们只需每隔 30 秒安排一次 Sidekiq 作业,以检查刷新率高于我们阈值的用户 table,然后终止所有活动会话。

如何终止会话取决于您如何管理它们 - 在我们的例子中,我们可以简单地向用户添加一个标志,上面写着“rate_limited”,并将我们的 Sidekiq 作业设置为 true,并且然后在每个请求上检查此标志的值。如果为真,用户将被重定向离开缓慢的页面并进入登录屏幕,该屏幕将愉快地每秒刷新 6 次。

即使没有请求记录,您也可以实现类似的功能 table,例如通过在用户 table.

的新列中跟踪请求率

请注意,此解决方案的用户体验优于 Nginx rate-limiting,因为用户实际上从未被锁定在应用程序之外。