網站被***是一個永恆不變的話題,網站***的方式也是一個永恆不變的老套路。找幾百個電腦(肉雞),控制這些電腦同時訪問你的網站,超過你網站的最大承載能力,然後你就癱了。方法雖然老土,但卻一直都很管用,就像怎麼打敗美帝國主義,最簡單的方法就是 13 億中國人都移民去美帝,吃他的、用他的、花他的,直接能讓美帝破產,壓根不需要用武力。土方法,自然解決這個問題的方法也土,就是設置一個海關,控制進入的人口數量,只要不讓你進來太多人,你就吃不窮他。 網站也一樣,只要控制能夠進入的訪問數量,你就***不了我。
Nginx 有 2 個模塊用於控制訪問“數量”和“速度”,簡單的說,控制你最多同時有 多少個訪問,並且控制你每秒鐘最多訪問多少次, 你的同時併發訪問不能太多,也不能太快,不然就“殺無赦”。
HttpLimitZoneModule 限制同時併發訪問的數量
HttpLimitReqModule 限制訪問數據,每秒內最多幾個請求
請先檢查你的 nginx 是否有這 2 個模塊,否則~~~額~~~沒戲,一邊牆角哭去吧~~~
1. 普通配置
啥叫普通配置 ? 就是說,你的服務器直接面向普通用戶,例如 普通用戶IE瀏覽器 ——-> 你的服務器,這也是你能 Google 到的 99% 的Nginx 安全配置例子,基本上就是抄了 Nginx 官方的一個 Sample。考慮到文章的完整性,我這裏也就重複一下這個被 千百個網站 已經重複過無數次的 配置
普通用戶IE瀏覽器 ——-> 你的服務器 由於普通用戶直接訪問你的服務器,所以你可以直接得到用戶的 IP 地址, 而我們的限制就是基於對 來源IP 地址的訪問限制
Nginx 裏面設置一個限制
Shell
網站被***是一個永恆不變的話題,網站***的方式也是一個永恆不變的老套路。找幾百個電腦(肉雞),控制這些電腦同時訪問你的網站,超過你網站的最大承載能力,然後你就癱了。方法雖然老土,但卻一直都很管用,就像怎麼打敗美帝國主義,最簡單的方法就是 13 億中國人都移民去美帝,吃他的、用他的、花他的,直接能讓美帝破產,壓根不需要用武力。土方法,自然解決這個問題的方法也土,就是設置一個海關,控制進入的人口數量,只要不讓你進來太多人,你就吃不窮他。 網站也一樣,只要控制能夠進入的訪問數量,你就***不了我。
Nginx 有 2 個模塊用於控制訪問“數量”和“速度”,簡單的說,控制你最多同時有 多少個訪問,並且控制你每秒鐘最多訪問多少次, 你的同時併發訪問不能太多,也不能太快,不然就“殺無赦”。
HttpLimitZoneModule 限制同時併發訪問的數量
HttpLimitReqModule 限制訪問數據,每秒內最多幾個請求
請先檢查你的 nginx 是否有這 2 個模塊,否則~~~額~~~沒戲,一邊牆角哭去吧~~~
1. 普通配置
啥叫普通配置 ? 就是說,你的服務器直接面向普通用戶,例如 普通用戶IE瀏覽器 ——-> 你的服務器,這也是你能 Google 到的 99% 的Nginx 安全配置例子,基本上就是抄了 Nginx 官方的一個 Sample。考慮到文章的完整性,我這裏也就重複一下這個被 千百個網站 已經重複過無數次的 配置
普通用戶IE瀏覽器 ——-> 你的服務器 由於普通用戶直接訪問你的服務器,所以你可以直接得到用戶的 IP 地址, 而我們的限制就是基於對 來源IP 地址的訪問限制
Nginx 裏面設置一個限制
Shell
## 用戶的 IP 地址 $binary_remote_addr 作爲 Key,每個 IP 地址最多有 50 個併發連接
## 你想開 幾千個連接 刷死我? 超過 50 個連接,直接返回 503 錯誤給你,根本不處理你的請求了
limit_conn_zone $binary_remote_addr zone=TotalConnLimitZone:10m ;
limit_conn TotalConnLimitZone 50;
limit_conn_log_level notice;
## 用戶的 IP 地址 $binary_remote_addr 作爲 Key,每個 IP 地址每秒處理 10 個請求
## 你想用程序每秒幾百次的刷我,沒戲,再快了就不處理了,直接返回 503 錯誤給你
limit_req_zone $binary_remote_addr zone=ConnLimitZone:10m rate=10r/s;
limit_req_log_level notice;
## 具體服務器配置
server {
listen 80;
location ~ \.php$ {
## 最多 5 個排隊, 由於每秒處理 10 個請求 + 5個排隊,你一秒最多發送 15 個請求過來,再多就直接返回 503 錯誤給你了
limit_req zone=ConnLimitZone burst=5 nodelay;
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
include fastcgi_params;
}
}
這樣一個最簡單的服務器安全限制訪問就完成了,這個基本上你 Google 一搜索能搜索到 90% 的網站都是這個例子,而且還強調用“$binary_remote_addr”可以節省內存之類的廢話
2. 你 Google 不到的配置
很多時候,我們的網站不是簡單的 普通用戶IE瀏覽器 ——-> 你的服務器 的結構, 考慮到網絡訪問速度問題,我們中間可能會有各種 網絡加速(CDN)。以本網站 www.bzfshop.net 爲例,考慮到網站的安全性和訪問加速,我們的架構是:
普通用戶瀏覽器 —–> 360網站衛士加速(CDN,360防 CC,DOS***) ——> 阿里雲加速服務器(我們自己建的CDN,阿里雲盾) —-> 源服務器(PHP 程序部署在這裏,iptables, nginx 安全配置)
可以看到,我們的網站中間經歷了好幾層的透明加速和安全過濾, 這種情況下,我們就不能用上面的“普通配置”。因爲上面基於 源IP的限制 結果就是,我們把 360網站衛士 或者 阿里雲盾 給限制了,因爲這裏“源IP”地址不再是 普通用戶的IP,而是中間 網絡加速服務器 的IP地址。我們需要限制的是 最前面的普通用戶,而不是中間爲我們做加速的 加速服務器。
2.1 現在我們面對的最直接的問題就是, 經過這麼多層加速,我怎麼得到“最前面普通用戶的 IP 地址”呢?
(這裏只說明結果,不瞭解 Http 協議的人請自行 Google 或者 Wikipedia http://zh.wikipedia.org/zh-cn/X-Forwarded-For )
當一個 CDN 或者透明代理服務器把用戶的請求轉到後面服務器的時候,這個 CDN 服務器會在 Http 的頭中加入 一個記錄
X-Forwarded-For : 用戶IP, 代理服務器IP
如果中間經歷了不止一個 代理服務器,像 www.bzfshop.net 中間建立多層代理之後,這個 記錄會是這樣
X-Forwarded-For : 用戶IP, 代理服務器1-IP, 代理服務器2-IP, 代理服務器3-IP, ….
可以看到經過好多層代理之後, 用戶的真實IP 在第一個位置, 後面會跟一串 中間代理服務器的IP地址,從這裏取到用戶真實的IP地址,針對這個 IP 地址做限制就可以了,
2.2 經過多層CDN之後取得原始用戶的IP地址,nginx 配置
取得用戶的原始地址 Shell
map $http_x_forwarded_for $clientRealIp {
## 沒有通過代理,直接用 remote_addr
"" $remote_addr;
## 用正則匹配,從 x_forwarded_for 中取得用戶的原始IP
## 例如 X-Forwarded-For: 202.123.123.11, 208.22.22.234, 192.168.2.100,...
## 這裏第一個 202.123.123.11 是用戶的真實 IP,後面其它都是經過的 CDN 服務器
~^(?P<firstAddr>[0-9\.]+),?.*$ $firstAddr;
}
## 通過 map 指令,我們爲 nginx 創建了一個變量 $clientRealIp ,這個就是 原始用戶的真實 IP 地址,
## 不論用戶是直接訪問,還是通過一串 CDN 之後的訪問,我們都能取得正確的原始IP地址
2.3 測試、測試
很多時候,你在網上搜到一堆配置,你照着做了,但是你怎麼知道這個配置真的正確 ?是的,我們需要自己做一個有效的真實的測試,驗證它是正確的之後才真的採用它
Nginx 這種配置怎麼測試呢? 用 Echo 模塊,如果你知道 Nginx 這個模塊的話。
以 www.bzfshop.net 網站爲例, 我們首先測試這個 $clientRealIp 是否真的是我們客戶機的 IP 地址,在網站上增加一個訪問地址,比如 www.bzfshop.net/nginx-test,配置如下:
給 Nginx 增加一個測試地址 Shell
server {
listen 80;
server_name www.bzfshop.net;
## 當用戶訪問 /nginx-test 的時候,我們輸出 $clientRealIp 變量,看看這個變量
## 值是不是真的 用戶源IP 地址
location /nginx-test {
echo $clientRealIp;
}
}
接下來,用你的瀏覽器訪問 www.bzfshop.net/nginx-test,這個時候會彈出框下載一個文件 nginx-test,下載完成用 notepad++ 打開,裏面就是一個 IP 地址
訪問 www.ip138.com ,看看這個裏面記錄的IP地址是否和 ip138 偵測的IP 一致?
通過這種方式,你就可以對 Nginx 的一些複雜配置做有效的測試。
經過測試,我們確認 通過多層CDN 之後,$clientRealIp 仍然是有效的原始用戶IP地址
2.4 根據用戶的真實 IP 做連接限制
下面是修改之後的 Nginx 配置:
CDN環境下 Nginx 的安全配置 Shell
## 這裏取得原始用戶的IP地址
map $http_x_forwarded_for $clientRealIp {
"" $remote_addr;
~^(?P<firstAddr>[0-9\.]+),?.*$ $firstAddr;
}
## 針對原始用戶 IP 地址做限制
limit_conn_zone $clientRealIp zone=TotalConnLimitZone:20m ;
limit_conn TotalConnLimitZone 50;
limit_conn_log_level notice;
## 針對原始用戶 IP 地址做限制
limit_req_zone $clientRealIp zone=ConnLimitZone:20m rate=10r/s;
#limit_req zone=ConnLimitZone burst=10 nodelay;
limit_req_log_level notice;
## 具體服務器配置
server {
listen 80;
location ~ \.php$ {
## 最多 5 個排隊, 由於每秒處理 10 個請求 + 5個排隊,你一秒最多發送 15 個請求過來,再多就直接返回 503 錯誤給你了
limit_req zone=ConnLimitZone burst=5 nodelay;
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
include fastcgi_params;
}
}
後記:
通過上面的配置,現在你的網站可以完美的配合任何 網絡加速服務(CDN)的使用,並且同時能保證對“最終用戶的限制”。
寫這篇文章的原因是因爲 我們最近把 www.bzfshop.net 遷移到 360網站衛士(wangzhan.360.cn) 上了,使用 360網站衛士 做我們的加速服務器和安全保護,同時我們網站自身 nginx 本身也配置了防止***的安全措施, 結果我們的安全配置把 360網站衛士的加速服務器給 盾 掉了,因爲所有用戶的訪問都通過加速服務器過來,很明顯加速服務器超過了我們的“連接限制”。經過上面的改造之後,現在我們的 Nginx 安全配置能夠和 360加速服務器 完美配合,同時能對終端的用戶訪問作限制。
寫下這些文字,希望對看到這篇文章的朋友會有用
## 用戶的 IP 地址 $binary_remote_addr 作爲 Key,每個 IP 地址最多有 50 個併發連接
## 你想開 幾千個連接 刷死我? 超過 50 個連接,直接返回 503 錯誤給你,根本不處理你的請求了
limit_conn_zone $binary_remote_addr zone=TotalConnLimitZone:10m ;
limit_conn TotalConnLimitZone 50;
limit_conn_log_level notice;
## 用戶的 IP 地址 $binary_remote_addr 作爲 Key,每個 IP 地址每秒處理 10 個請求
## 你想用程序每秒幾百次的刷我,沒戲,再快了就不處理了,直接返回 503 錯誤給你
limit_req_zone $binary_remote_addr zone=ConnLimitZone:10m rate=10r/s;
limit_req_log_level notice;
## 具體服務器配置
server {
listen 80;
location ~ \.php$ {
## 最多 5 個排隊, 由於每秒處理 10 個請求 + 5個排隊,你一秒最多發送 15 個請求過來,再多就直接返回 503 錯誤給你了
limit_req zone=ConnLimitZone burst=5 nodelay;
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
include fastcgi_params;
}
}
這樣一個最簡單的服務器安全限制訪問就完成了,這個基本上你 Google 一搜索能搜索到 90% 的網站都是這個例子,而且還強調用“$binary_remote_addr”可以節省內存之類的廢話
2. 你 Google 不到的配置
很多時候,我們的網站不是簡單的 普通用戶IE瀏覽器 ——-> 你的服務器 的結構, 考慮到網絡訪問速度問題,我們中間可能會有各種 網絡加速(CDN)。以本網站 www.bzfshop.net 爲例,考慮到網站的安全性和訪問加速,我們的架構是:
普通用戶瀏覽器 —–> 360網站衛士加速(CDN,360防 CC,DOS***) ——> 阿里雲加速服務器(我們自己建的CDN,阿里雲盾) —-> 源服務器(PHP 程序部署在這裏,iptables, nginx 安全配置)
可以看到,我們的網站中間經歷了好幾層的透明加速和安全過濾, 這種情況下,我們就不能用上面的“普通配置”。因爲上面基於 源IP的限制 結果就是,我們把 360網站衛士 或者 阿里雲盾 給限制了,因爲這裏“源IP”地址不再是 普通用戶的IP,而是中間 網絡加速服務器 的IP地址。我們需要限制的是 最前面的普通用戶,而不是中間爲我們做加速的 加速服務器。
2.1 現在我們面對的最直接的問題就是, 經過這麼多層加速,我怎麼得到“最前面普通用戶的 IP 地址”呢?
(這裏只說明結果,不瞭解 Http 協議的人請自行 Google 或者 Wikipedia http://zh.wikipedia.org/zh-cn/X-Forwarded-For )
當一個 CDN 或者透明代理服務器把用戶的請求轉到後面服務器的時候,這個 CDN 服務器會在 Http 的頭中加入 一個記錄
X-Forwarded-For : 用戶IP, 代理服務器IP
如果中間經歷了不止一個 代理服務器,像 www.bzfshop.net 中間建立多層代理之後,這個 記錄會是這樣
X-Forwarded-For : 用戶IP, 代理服務器1-IP, 代理服務器2-IP, 代理服務器3-IP, ….
可以看到經過好多層代理之後, 用戶的真實IP 在第一個位置, 後面會跟一串 中間代理服務器的IP地址,從這裏取到用戶真實的IP地址,針對這個 IP 地址做限制就可以了,
2.2 經過多層CDN之後取得原始用戶的IP地址,nginx 配置
取得用戶的原始地址 Shell
map $http_x_forwarded_for $clientRealIp {
## 沒有通過代理,直接用 remote_addr
"" $remote_addr;
## 用正則匹配,從 x_forwarded_for 中取得用戶的原始IP
## 例如 X-Forwarded-For: 202.123.123.11, 208.22.22.234, 192.168.2.100,...
## 這裏第一個 202.123.123.11 是用戶的真實 IP,後面其它都是經過的 CDN 服務器
~^(?P<firstAddr>[0-9\.]+),?.*$ $firstAddr;
}
## 通過 map 指令,我們爲 nginx 創建了一個變量 $clientRealIp ,這個就是 原始用戶的真實 IP 地址,
## 不論用戶是直接訪問,還是通過一串 CDN 之後的訪問,我們都能取得正確的原始IP地址
2.3 測試、測試
很多時候,你在網上搜到一堆配置,你照着做了,但是你怎麼知道這個配置真的正確 ?是的,我們需要自己做一個有效的真實的測試,驗證它是正確的之後才真的採用它
Nginx 這種配置怎麼測試呢? 用 Echo 模塊,如果你知道 Nginx 這個模塊的話。
以 www.bzfshop.net 網站爲例, 我們首先測試這個 $clientRealIp 是否真的是我們客戶機的 IP 地址,在網站上增加一個訪問地址,比如 www.bzfshop.net/nginx-test,配置如下:
給 Nginx 增加一個測試地址 Shell
server {
listen 80;
server_name www.bzfshop.net;
## 當用戶訪問 /nginx-test 的時候,我們輸出 $clientRealIp 變量,看看這個變量
## 值是不是真的 用戶源IP 地址
location /nginx-test {
echo $clientRealIp;
}
}
接下來,用你的瀏覽器訪問 www.bzfshop.net/nginx-test,這個時候會彈出框下載一個文件 nginx-test,下載完成用 notepad++ 打開,裏面就是一個 IP 地址
訪問 www.ip138.com ,看看這個裏面記錄的IP地址是否和 ip138 偵測的IP 一致?
通過這種方式,你就可以對 Nginx 的一些複雜配置做有效的測試。
經過測試,我們確認 通過多層CDN 之後,$clientRealIp 仍然是有效的原始用戶IP地址
2.4 根據用戶的真實 IP 做連接限制
下面是修改之後的 Nginx 配置:
CDN環境下 Nginx 的安全配置 Shell
## 這裏取得原始用戶的IP地址
map $http_x_forwarded_for $clientRealIp {
"" $remote_addr;
~^(?P<firstAddr>[0-9\.]+),?.*$ $firstAddr;
}
## 針對原始用戶 IP 地址做限制
limit_conn_zone $clientRealIp zone=TotalConnLimitZone:20m ;
limit_conn TotalConnLimitZone 50;
limit_conn_log_level notice;
## 針對原始用戶 IP 地址做限制
limit_req_zone $clientRealIp zone=ConnLimitZone:20m rate=10r/s;
#limit_req zone=ConnLimitZone burst=10 nodelay;
limit_req_log_level notice;
## 具體服務器配置
server {
listen 80;
location ~ \.php$ {
## 最多 5 個排隊, 由於每秒處理 10 個請求 + 5個排隊,你一秒最多發送 15 個請求過來,再多就直接返回 503 錯誤給你了
limit_req zone=ConnLimitZone burst=5 nodelay;
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
include fastcgi_params;
}
}
後記:
通過上面的配置,現在你的網站可以完美的配合任何 網絡加速服務(CDN)的使用,並且同時能保證對“最終用戶的限制”。