乐趣区

关于Laravel-与-Nginx-限流策略防止恶意请求

一、问题背景

最近公司最近的几台线上服务器经常出现 CPU 覆盖过高, 影响部分应用响应超时, 产生了大量的短信和邮件报警, 经过排查数据库日志和 access.log, 发现是 API 接口被刷, 被恶意疯狂请求, 最大一次大概 120 次 /s。

之前没有过太多这方面经验, 处理起来不是很顺畅, 这次的问题刚好提了醒, 经过这次的问题暴露, 来记录一下解决方案和策略。

线上的部署方案是:nginx + laravel。

首先我们尝试从 nginx 层面入手, 将占用更少的内存消耗, 无需再转发到 php-fpm 上处理。

二、(恶意)请求特征

想好很好的特征, 就必须捕捉一定的特征, 通过这个特征来有效的控制到恶意请求。

  1. 短时间内,IP 对某接口产生大量请求
  2. user_agent, 非正常信息或为空
  3. 请求量比平时要高涨很多。

三、限流策略(nginx)

限制请求数

首先的话. 就是控制单 IP 时间上的请求次数和 IP 连接数, 配置如下:

http {
    limit_req_zone $binary_remote_addr zone=one:1m rate=1r/s;

    server {
        location /api/ {limit_req zone=one burst=5;}
    }
}

limit_req_zone 主要控制单个 IP 的请求速率, 使用漏桶算法来完成限制,limit_req_zone size, 主要用于储存统计 IP 的请求信息,1M 可以储存 16000 个 IP, 当每秒的请求超过了 16000 个的时候, 其余访问都会被访问 503 服务暂不可用。

上面的模板设置了, 每秒最大不超过 1 个请求, 最大延迟请求不超过 5 个。

如果我们的服务器每次接口的响应时间是在 200ms-300ms, 那我们对应的每秒的限制就应该设置成 1000ms / 接口响应耗时

限制并发连接数

限制完用户请求频率后, 如果仍然还是存在很大的恶意请求, 我们还可以进行并发数的限制。

http {
    limit_conn_zone $binary_remote_addr zone=one:1m;

    server {
        location /api/ {limit_conn one 10;}
    }
}

limit_conn_zone:主要是用于控制请求并发数, 频率不能太快。

limit_conn_zone size 跟 limit_req_zone 意思一致, 可根据需要动态调控, 上面的案例中, 表示限制每个客户端 IP 最大并发连接数为 10。

设定 IP 黑名单

当某个 IP 请求过于频繁或者需要完全杜绝该 IP 的访问时, 可以通过 nginx 的 deny 配置来禁止黑名单中的 IP 访问。

http {include blockip.conf;}

黑名单配置

deny 195.91.112.66;
deny 192.168.2.100;

被添加黑名单后, 再次访问就会出现 403 禁止访问.

限制 UA(user-agent)信息

http {
    server {if ($http_user_agent ~* "curl") {return 403;}
    }
}

上面就禁止了 ua 信息为 curl 的客户端, 直接返回 403。

禁止多个 ua, 通过 | 来隔断。

if ($http_user_agent ~* "curl|wget") {return 403;}

(四)、限流策略(laravel)

在我们的 laravel 项目中, 存在一个 Throttle 中间件, 该策略, 可以在应用层上面, 有效抑制用户可以恶意请求, 配置如下:

Route::group(['middleware' => 'throttle:30:1'],function(){Route::any('/login', 'LoginController@login');
});

在 throttle 配置中, 第一个参数控制了请求数, 第二个参数用于控制请求频率, 上面的配置表明, 每个客户端 IP 每分钟最大请求 30 次 login路由。

当客户端 ip 超出请求限制后, 服务端就会返回 429 Too Many Attempts. 响应

退出移动版