網站安全配置Nginx防止網站被攻擊(轉)

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

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

}

## 用戶的 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;
    }    

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49

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

  1. 你 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地址

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地址

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

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;
        }
}

server {
    listen   80;
        server_name  www.bzfshop.net;

        ## 當用戶訪問 /nginx-test 的時候,我們輸出 $clientRealIp 變量,看看這個變量
        ## 值是不是真的 用戶源IP 地址
        location /nginx-test {
                echo $clientRealIp;
        }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

接下來,用你的瀏覽器訪問 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;
    }   

}

## 這裏取得原始用戶的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;
    }    

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61


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