(轉)nginx在CDN加速以後,獲取用戶真實IP作併發訪問限制的方法 | 張戈博客

《Nginx在CDN加速以後,獲取用戶真實IP作併發訪問限制的方法》來自張戈博客
 

最近一直在幫一個購買了張戈博客付費服務的朋友作網站防禦,爲了簡單抵擋一下競爭對手的DDoS攻擊,他給網站開啓了Incapsula的免費CDN服務。php

開啓CDN以後,我以前給他寫的Shell防禦腳本也就宣告無效了,由於不論是正常訪問仍是攻擊訪問,腳本拿到的IP都是CDN節點的,而我不可能把CDN的節點IP也給禁用了,那就都不能訪問了。html

爲了解決這個窘迫,我想到了之前看過的Nginx訪問限制。經過查資料,讓我拜讀了一枚大神的神做,感受收穫頗豐!因而轉過來整理一下,分享給更多須要的人!node

Nginx 有2個模塊用於控制訪問「數量」和「速度」,簡單的說,控制你最多同時有 多少個訪問,而且控制你每秒鐘最多訪問多少次, 你的同時併發訪問不能太多,也不能太快,否則就「殺無赦」。nginx

HttpLimitZoneModule    限制同時併發訪問的數量git

HttpLimitReqModule      限制訪問數據,每秒內最多幾個請求github

1、普通配置

什麼叫普通配置?shell

普通配置就是針對【用戶瀏覽器】→【網站服務器】這種常規模式的nginx配置。那麼,若是我要對單IP作訪問限制,絕大多數教程都是這樣寫的:瀏覽器

 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 
21 
22 
23 
## 用戶的 IP 地址 $binary_remote_addr 做爲 Key,每一個 IP 地址最多有 50 個併發鏈接
## 你想開 幾千個鏈接 刷死我? 超過 50 個鏈接,直接返回 503 錯誤給你,根本不處理你的請求了
limit_conn_zone $binary_remote_addrzone=TotalConnLimitZone:10m;
limit_conn  TotalConnLimitZone50;
limit_conn_log_level notice;
 
## 用戶的 IP 地址 $binary_remote_addr 做爲 Key,每一個 IP 地址每秒處理 10 個請求
## 你想用程序每秒幾百次的刷我,沒戲,再快了就不處理了,直接返回 503 錯誤給你
limit_req_zone $binary_remote_addrzone=ConnLimitZone:10mrate=10r/s;
limit_req_log_level notice;
 
## 具體服務器配置
server{
listen80;
location~\.php${
## 最多 5 個排隊, 因爲每秒處理 10 個請求 + 5個排隊,你一秒最多發送 15 個請求過來,再多就直接返回 503 錯誤給你了
limit_req zone=ConnLimitZone burst=5nodelay;
fastcgi_pass 127.0.0.1:9000;
fastcgi_index  index.php;
include fastcgi_params;
}
 
}

這樣一個最簡單的服務器安全限制訪問就完成了,這個基本上你 Google 一搜索能搜索到  90% 的網站都是這個例子,並且還強調用「$binary_remote_addr」能夠節省內存之類的云云。安全

2、CDN以後

目前國內已經爭相出現了百度雲加速、加速樂、360網站衛士以及安全寶等免費CDN。讓咱們這些小網站也能免費享受之前高大上的CDN加速服務。服務器

因而,網站的訪問模式就變爲:

用戶瀏覽器 → CDN節點 → 網站源服務器

甚至是更復雜的模式:

用戶瀏覽器 → CDN節點(CDN入口、CC\DDoS攻擊流量清洗等) → 阿里雲盾 → 源服務器

能夠看到,咱們的網站中間經歷了好幾層的透明加速和安全過濾, 這種狀況下,咱們就不能用上面的「普通配置」。由於普通配置中基於【源IP的限制】的結果就是,咱們把【CDN節點】或者【阿里雲盾】給限制了,由於這裏「源IP」地址再也不是真實用戶的IP,而是中間CDN節點的IP地址。

咱們須要限制的是最前面的真實用戶,而不是中間爲咱們作加速的加速服務器。

其實,當一個 CDN 或者透明代理服務器把用戶的請求轉到後面服務器的時候,這個 CDN 服務器會在 Http 的頭中加入一個記錄

X-Forwarded-For :  用戶IP, 代理服務器IP

若是中間經歷了不止一個代理服務器,這個記錄會是這樣

X-Forwarded-For :  用戶IP, 代理服務器1-IP, 代理服務器2-IP, 代理服務器3-IP, ….

能夠看到通過好多層代理以後, 用戶的真實IP 在第一個位置, 後面會跟一串中間代理服務器的IP地址,從這裏取到用戶真實的IP地址,針對這個 IP 地址作限制就能夠了。

那麼針對CDN模式下的訪問限制配置就應該這樣寫:

 

 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 
21 
22 
23 
24 
25 
26 
27 
28 
29 
## 這裏取得原始用戶的IP地址
map$http_x_forwarded_for$clientRealIp{
""$remote_addr;
~^(?P<firstAddr>[0-9\.]+),?.*$$firstAddr;
}
 
## 針對原始用戶 IP 地址作限制
limit_conn_zone $clientRealIpzone=TotalConnLimitZone:20m;
limit_conn  TotalConnLimitZone50;
limit_conn_log_level notice;
 
## 針對原始用戶 IP 地址作限制
limit_req_zone $clientRealIpzone=ConnLimitZone:20mrate=10r/s;
#limit_req zone=ConnLimitZone burst=10 nodelay; #若是開啓此條規則,burst=10的限制將會在nginx全局生效
limit_req_log_level notice;
 
## 具體Server:以下在監聽php部分新增限制規則便可
server{
listen80;
location~\.php${
## 最多 5 個排隊, 因爲每秒處理 10 個請求 + 5個排隊,你一秒最多發送 15 個請求過來,再多就直接返回 503 錯誤給你了
limit_req zone=ConnLimitZone burst=5nodelay;
 
fastcgi_pass 127.0.0.1:9000;
fastcgi_index  index.php;
include fastcgi_params;
}
 
}

 

3、echo模塊

做者原文提到了nginx的一個echo模塊,特地玩了下感受挺有意思的,下面貼一下簡單集成步驟。

①、給nginx集成echo模塊:

 

 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 
21 
22 
23 
24 
25 
cd/usr/local/src
#下載echo模塊並解壓:
wget https://github.com/openresty/echo-nginx-module/archive/v0.57.tar.gz
tar zxvf v0.57.tar.gz
 
#下載nginx並解壓
wget http://nginx.org/download/nginx-1.6.0.tar.gz
tar-xzvf nginx-1.6.0.tar.gz
cdnginx-1.6.0/
 
#查看在用nginx的編譯參數(若是是全新安裝則省略)
/usr/local/nginx/sbin/nginx-V
nginx version:nginx/1.6.0
built by gcc4.4.720120313(Red Hat4.4.7-4)(GCC)#如下這行即爲舊的編譯參數:
configure arguments:--user=www--group=www--prefix=/usr/local/nginx--with-http_gzip_static_module
#在舊的編譯參數基礎上新增【--add-module=/echo模塊的解壓路徑】參數,開始編譯
./configure--prefix=/usr/local/nginx/nginx--add-module=/usr/local/src/echo-nginx-module-0.57
 
#make編譯
make-j2
 
#平滑升級nginx (若是是全新安裝請執行:make install)
mv/usr/local/nginx/sbin/nginx/usr/local/nginx/sbin/nginx.old
cp-fobjs/nginx/usr/local/nginx/sbin/
makeupgrade

 

②、echo用法舉例:

其實就和shell的echo差很少,可否輸出自定義信息。

好比,在nginx裏面配置以下:

 
location/hello{
echo"hello, world!";
}

訪問http://yourdomain.com/hello 就會在瀏覽器裏面輸出hello, world! 了(若是域名開了CDN可能會報404)。

又好比,測試本文提到的真實用戶的IP,只要在本文第二步配置基礎上,加上以下規則並reload便可:

 
10 
server{
listen80;
server_name  yourdomain.com;
## 如下是新增規則:
## 當用戶訪問 /realip 的時候,咱們輸出 $clientRealIp 變量,看看這個變量
## 值是否是真的 用戶源IP 地址
location/realip{
echo$clientRealIp;
}
}

生效後,訪問http://yourdomain.com/realip 就能顯示你所用電腦(寬帶)的真實IP了(可在ip138查驗準確性):

Ps:感興趣又喜歡學習的童鞋能夠看下相關wiki文檔:http://wiki.nginx.org/HttpEchoModule

本文介紹到這就差很少結束了,也是在神做的基礎上精簡整理並測試的,若是看完還有些許疑問,請前往查看神做原文,也許仍是大神寫的比較好理解(是不是原創我就不深究了,感受也是轉來轉去,都沒留連接,悲哀的互聯網)!

本文整理自【棒主婦開源】,原文地址:《網站安全配置(Nginx)防止網站被攻擊》。

相關文章
相關標籤/搜索