簡單的Nginx防CC方式
實驗
Nginx配置
http { limit_req_zone $binary_remote_addr zone=one:10m rate=1r/s; server { #限制每ip每秒不超過20個請求,漏桶數(shù)burst為5 #brust的意思就是,如果第1秒、2,3,4秒請求為19個, #第5秒的請求為25個是被允許的。 #但是如果你第1秒就25個請求,第2秒超過20的請求返回503錯誤。 #nodelay,如果不設(shè)置該選項,嚴格使用平均速率限制請求數(shù), #第1秒25個請求時,5個請求放到第2秒執(zhí)行, #設(shè)置nodelay,25個請求將在第1秒執(zhí)行。 limit_req zone=one burst=1 nodelay; }}
上面樣本的配置是什么意思呢?
詳細的可以參考官方說明文檔:Module ngx_http_limit_req_module(http://nginx.org/en/docs/http/ngx_http_limit_req_module.html)
模擬請求
這里我們需要Apache Benchmark這個小工具來生成請求
//1個用戶持續(xù)100s的時間向服務(wù)器發(fā)送請求
ab -t 100 -c 1 -vvv http://example.com/
Nginx配置樣本一
http { limit_req_zone $binary_remote_addr zone=one:10m rate=1r/s; server { limit_req zone=one burst=1 nodelay; }}
ab測試結(jié)果如下所示:
數(shù)據(jù) | 成功的請求數(shù) | 失敗的請求數(shù) | 請求時間 | 每秒成功的請求數(shù) |
---|
1 | 100 | 19438 | 101.195 | 0.98 |
2 | 100 | 17651 | 100.655 | 0.99 |
3 | 97 | 25735 | 100.424 | 0.96 |
4 | 101 | 26791 | 100.000 | 1.01 |
5 | 98 | 19051 | 100.514 | 0.98 |
平均 | 99 | 21733.2 | 100.557 | 0.98 |
以上失敗的請求在Nginx上生成的錯誤日志如下顯示
2015/05/0912:48:57 [error] 6564#0: *2219 limiting requests, excess: 1.273 by zone "one", client: 10.0.2.2, server: example.com, request: "GET / HTTP/1.0", host: "example.com"2015/05/0912:48:57 [error] 6564#0: *2220 limiting requests, excess: 1.272 by zone "one", client: 10.0.2.2, server: example.com, request: "GET / HTTP/1.0", host: "example.com"2015/05/0912:48:57 [error] 6564#0: *2221 limiting requests, excess: 1.271 by zone "one", client: 10.0.2.2, server: example.com, request: "GET / HTTP/1.0", host: "example.com"2015/05/0912:48:57 [error] 6564#0: *2222 limiting requests, excess: 1.270 by zone "one", client: 10.0.2.2, server: example.com, request: "GET / HTTP/1.0", host: "example.com"2015/05/0912:48:57 [error] 6564#0: *2223 limiting requests, excess: 1.269 by zone "one", client: 10.0.2.2, server: example.com, request: "GET / HTTP/1.0", host: "example.com"2015/05/0912:48:57 [error] 6564#0: *2224 limiting requests, excess: 1.268 by zone "one", client: 10.0.2.2, server: example.com, request: "GET / HTTP/1.0", host: "example.com"
如上ab測試中如果是失敗的請求,nginx的limit_req模塊會統(tǒng)一返回503給客戶端,瀏覽器上面顯示的是這個樣子的。

Nginx配置樣本二
在配置二里面,我把burst(峰值)提高到了10
http { limit_req_zone $binary_remote_addr zone=one:10m rate=1r/s; server { limit_req zone=one burst=10 nodelay; }}
數(shù)據(jù) | 成功的請求數(shù) | 失敗的請求數(shù) | 請求時間 | 每秒成功的請求數(shù) |
---|
1 | 110 | 19042 | 100.144 | 1.09 |
2 | 111 | 22271 | 101.714 | 1.09 |
3 | 111 | 18466 | 100.504 | 1.10 |
4 | 111 | 16468 | 101.285 | 1.09 |
5 | 111 | 12770 | 100.596 | 1.10 |
平均 | 110 | 17803 | 100.788 | 1.09 |
從數(shù)據(jù)來看,提高了burst值,明顯nginx成功的請求數(shù)上去了。
Nginx配置樣本三
在樣本二的基礎(chǔ)上,我們把nodelay去除掉
http { limit_req_zone $binary_remote_addr zone=one:10m rate=1r/s; server { limit_req zone=one burst=10; }}
數(shù)據(jù) | 成功的請求數(shù) | 失敗的請求數(shù) | 請求時間 | 每秒成功的請求數(shù) |
---|
1 | 96 | 0 | 100.223 | 1.09 |
2 | 98 | 0 | 100.238 | 0.97 |
3 | 100 | 0 | 100.761 | 0.99 |
4 | 96 | 0 | 100.074 | 0.95 |
5 | 97 | 0 | 100.021 | 0.96 |
平均 | 97.4 | 0 | 100.263 | 0.97 |
從這里的數(shù)據(jù)可以看到將nodelay的參數(shù)去掉的話,成功的請求數(shù)在100左右而失敗的請求數(shù)變成0了,為什么呢?
有nodelay參數(shù)的時候,nginx正常是及時處理當前的請求的并響應(yīng)數(shù)據(jù)給客戶端,但是如果超過limit_req_module的限制,那么會統(tǒng)一返回503給客戶端。
無nodelay參數(shù)的時候,nginx正常是及時處理當前的請求的并響應(yīng)數(shù)據(jù)給客戶端,但是如果超過limit_req_module的限制,那么會將此此請求緩存「就先這么理解」起來稍后再處理,所以也就不會出現(xiàn)大量的失敗請求數(shù)了。
存在的問題
雖然用limit_req_module可以一定上的防止CC攻擊,但是有誤殺概率;國內(nèi)寬帶用戶的IP地址已經(jīng)大量內(nèi)網(wǎng)化,幾百人共享一個IP的可能性是很大的。