首页 > 解决方案 > 如何使用来自 AJAX 请求的响应故意刷新或崩溃浏览器选项卡

问题描述

昨天我们将代码推送到生产环境,其中包括一个轮询机制(通过setInterval()Javascript),该机制每 15 秒发出一次 AJAX 请求,以使客户端与服务器保持同步。尽管今天在任何特定时间只有大约 450 人在使用我们的网站,但似乎我们的许多用户即使不使用我们的网站也会保持打开状态。就像,很多用户。

在周日的 12 小时内,我们自己有效地进行了 DDoS。大约 3,500 人在他们的浏览器上打开了我们的网站,这意味着每秒向这个 PHP 端点发送 200 个请求。KeepAlive 为 5,这会触发我们的 Apache 服务器快速达到其 MaxClients 限制,这会阻塞新连接的建立,从而导致现有用户出现随机错误等。我们提高了该限制并降低了 KeepAlive 时间而没有问题,但真正的一个小时后,当我们将其更改setInterval()为也考虑时,修复来了document.visibilityState == "visible",这样后台选项卡就不会通过轮询来锤击我们的服务器。(如果您对此感到疑惑,我们将转向静默推送通知,而不是比我们在这次体验后计划的更早进行轮询)。

该修复程序应该适用于新用户,但它让我们留下了那些仍然在他们的计算机上打开我们的网站的 3,500 名用户,即使他们不使用该网站,他们也会不分青红皂白地向我们发出请求。我们需要他们尽快获取新代码以停止 DDoS,或者让他们的标签冻结,从而停止来自浏览器的 Web 请求。我们在 Chrome 和 Safari 上测试了几个想法,但都没有奏效。

第一个是通过 PHP 的header("Refresh:0");. 我们尝试在我们的端点中包含一些这样的变体,但似乎来自 AJAX 请求的响应标头不会引发页面刷新。我们还尝试使用 HTML 响应请求,echo '<meta http-equiv="refresh" content="0">';但这也不起作用,可能是因为 AJAX 请求需要 JSON,而不是 HTML,并且更改响应的内容类型还不够。

第二个是通过用数据重载对该端点的响应来使页面崩溃。我们尝试将多个bin2hex(openssl_random_pseudo_bytes(5000000))s 作为变量添加到响应中,这些变量会写入浏览器的本地存储中。这确实让浏览器冻结并使用多达 1GB 的 RAM,但即使界面完全没有响应,选项卡也没有“崩溃”并且 Web 请求继续发出,所以这种方法也不起作用。

更新:我们尝试的第三件事是sleep(9999999)在他们遇到的 PHP 文件中做一个。由于浏览器最多只能同时向给定域发出 6 个请求,我们认为一旦这些客户端向端点发出 6 个请求,将不会发出进一步的请求,因为这 6 个将无限期挂起。我们尝试将其推送到生产环境,但情况并不顺利:在 30 秒内,Apache 的负载比以前更加严重,因为现在请求堆积如山,没有完成。所以我们不得不重启 Apache(这反过来又取消了所有挂起的请求,让我们回到之前的状态)。我们认为使用浏览器最多只能同时向域发出 6 个请求这一事实的一些变体可能会起作用,但我们不确定如何使用该事实。

我们还能尝试什么?

标签: javascriptphpajaxgoogle-chromesafari

解决方案


(我太新了,无法发表评论,所以我必须将其作为答案)

通常在服务器级别而不是在应用程序级别处理请求至少便宜一个数量级。鉴于您的应用程序可能会访问数据库、恢复会话、执行大量路由等,然后才能拒绝请求。

我仍然建议弃用有问题的网址。如果您返回 HTTP 410 GONE 而不是 404 并添加缓存控制标头,您可能会说服浏览器从缓存中提供结果而不是实际进行调用。

缓存控制:公共,最大年龄=31536000

这假设您当然在轮询机制中没有使用缓存破坏者参数。如果每个 url 都是新的和唯一的,那么缓存不会拯救你。


推荐阅读