天天看點

Nginx中對同一IP限速限流DDOS預防

作用:

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;

# ...

}

}

這個例子同時使用了​​geo​​​和​​map​​指令。對于IP位址在白名單中的,geo塊配置設定0值給$limit;其它所有不在白名單中的IP位址,配置設定1值。然後我們使用一個map去将這些值映射到某個key中,例如:

  • 如果$limit是0,$limit_key被設定為空字元串
  • 如果$limit是1,$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的錯誤日志如下:

繼續閱讀