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

 

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