網站安全配置(Nginx)防止網站被***(包括使用了CDN加速之後的配置方法)

網站被***是一個永恆不變的話題,網站***的方式也是一個永恆不變的老套路。找幾百個電腦(肉雞),控制這些電腦同時訪問你的網站,超過你網站的最大承載能力,然後你就癱了。方法雖然老土,但卻一直都很管用,就像怎麼打敗美帝國主義,最簡單的方法就是 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)的使用,並且同時能保證對“最終用戶的限制”。




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