Nginx中对同一IP限速限流DDOS预防

参考:https://www.phpmianshi.com/?id=97

作用:

Nginx通过limit_conn_zone和limit_req_zone对同一个IP地址进行限速限流,可防止DDOS/CC和flood攻击

 

limit_conn_zone是限制同一个IP的连接数,而一旦连接建立以后,客户端会通过这连接发送多次请求,那么limit_req_zone就是对请求的频率和速度进行限制。

 

(注意,tcp连接是有状态的,而构建在tcp之上的http却是无状态的协议)。

通过打开一个网页,然后通过wareshark可以看到,一个连接建立后(即三次握手后),在这个连接断开之前(即四次挥手之前),会有很多的http request,这就是他们的区别:即一个连接的生命周期中,会存在一个或者多个请求,这是为了加快效率,避免每次请求都要三次握手建立连接,现在的HTTP/1.1协议都支持这种特性,叫做keepalive。

 

首先看看限制连接数,限制每个ip的并发请求

在Nginx的http配置如下:

limit_conn_zone $binary_remote_addr zone=Addr:10m;

然后在Nginx的server段配置如下:

limit_conn Addr 2;

这里两行虽然不是在一起配置,它们之间通过Addr这个变量名联系在一起。你可以对某个目录或指定后缀比如.html或.jpg进行并发连接限制,因为不同资源连接数是不同的。

有了连接数限制,相当于限制了客户端浏览器和Nginx之间 的管道个数,那么浏览器通过这个管道运输请求,如同向自来水管中放水,水的流速和压力对于管道另外一端是有影响的。为了防止不信任的客户端通过这个管道疯狂发送请求,对我们的耗CPU的资源URL不断发出狂轰滥炸,必须对请求的速度进行限制,如同对水流速度限制一样。

限制每个ip的访问频率,在Nginx.conf的http段配置:

limit_req_zone $binary_remote_addr zone=one:10m rate=5r/s;

在Nginx.conf的server段配置

limit_req zone=one burst=10;

这里引入burst漏桶原理,结合rate速率每秒5个请求(rate=5r/s)解释如下:

  • rate=5r/s:从单一IP地址每秒5个请求是允许的,

  • burst=10:允许超过频率rate限制的请求数不多于10个

  • 当每秒请求超过5个,但是在10个以下,也就是每秒请求的数量在5到10之间的请求将被延时 delay,虽然这里没有明写delay,默认是延时,因为漏洞其实类似队列Queue或消息系统, 当每秒请求数量超过最低速率每秒5个时,多余的请求将会进入这个队列排队等待。如同机场安检,一次放入5个,多于5个,小于10个的排队等待,注意:这个队列或漏洞是以每秒为单位的

  • 如果每秒请求数超过10个,也就是burst的限制,那么也不排队了直接回绝,返回503错误。也就是说排队长度不能超过10个。

  • Zone — 定义了存储每个IP地址状态和它访问受限请求URL的频率的共享内存区域。将这些信息保存在共享内存中,意味着这些信息能够在NGINX工作进程之间共享。定义有两个部分:由zone=关键字标识的区域名称,以及冒号后面的区域大小。约16000个IP地址的状态信息消耗1M内存大小,因此我们的区域(zone)大概可以存储约160000个地址。当NGINX需要添加新的记录时,如果此时存储耗尽了,最老的记录会被移除。如果释放的存储空间还是无法容纳新的记录,NGINX返回503 (Service Temporarily Unavailable)状态码。此外,为了防止内存被耗尽,每次NGINX创建一个新的记录的同时移除多达两条前60秒内没有被使用的记录。

 

上述使用默认延时也就是队列的方式对于一个页面如果有很多资源需要加载,那么通过排队延时加载无疑对服务器冲击小,而且防止攻击者对同一个资源发出很多请求。

如果我们使用nodelay:

limit_req zone=one burst=10 nodelay;

这表示,如果每秒请求在5-10个之间会尽快完成,也就是以每秒10个速率完成,超过每秒10+5也就是15个就立即返回503,因此nodelay实际没有了延时,也就取消了队列等候过渡。

在Twitter Facebook LinkedIn这类大型网站中,由于访问量巨大,通常会在http服务器后面放置一个消息队列,比如Apache Kafka,用来排队大量请求,因此,对于中小型网站,推荐使用delay方案,而不要写明nodelay,但是网络上其他各种文章几乎都是推荐nodelay.

最后一个带宽限制,如下:

limit_rate 50k;limit_rate_after 500k;

当下载的大小超过500k以后,以每秒50K速率限制。

 

只开一个连接:

ab -n100 -c100 -k https//www.phpmianshi.com/test.php

这里的-k选秀就是表示keepalive,只开一个连接来发送这100个请求,即使是同时发送,那么server也不会认为你超过了,因为在一个时间你只是建立一个连接,这样这100个请求都会干净利落的处理完成。

 

高级设置的例子

下面的例子展示了如何将限流作用在任何一个不在“白名单”中的请求上。

geo $limit {
    default 1;
    10.0.0.0/8 0;
    192.168.0.0/24 0;
}

​map $limit $limit_key {
    0 "";
    1 $binary_remote_addr;
}

limit_req_zone $limit_key zone=req_zone:10m rate=5r/s;

server {
    location / {
        limit_req zone=req_zone burst=10 nodelay;
        # ...
    }
}


 

这个例子同时使用了geomap指令。对于IP地址在白名单中的,geo块分配0值给$limit;其它所有不在白名单中的IP地址,分配1值。然后我们使用一个map去将这些值映射到某个key中,例如:

  • 如果$limit0$limit_key被设置为空字符串

  • 如果$limit1$limit_key被设置为客户端的IP地址的二进制格式

这个两个结合起来,对于白名单中的IP地址,$limit_key被设置为空字符串;否则,被设置为客户端的IP地址。当limit_req_zone指令的第一个参数是一个空字符串,限制不起作用,因此白名单的IP地址(在10.0.0.0/8和192.168.0.0/24子网中)没有被限制。其它所有的IP地址都被限制为5个请求每秒。

 

超过限流的请求会返回503

查看nginx的错误日志如下:

 [error] 120315#0: *32086 limiting requests, excess: 1.000 by zone "mylimit", client: 192.168.1.2

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章