Dos / DDos 阻斷服務攻擊 可能有兩種目的
一種是要讓你網站掛掉、無法正常服務。
另一是持續、大量下載你網站中圖片/影片,但又不讓你網站掛掉,然後你就得負擔大量的傳輸費用。
對於放在雲端的網站,如 GCP、AWS 等,用戶每下載 1GB 的網頁/圖片,站長得負擔約 USD 0.1 的費用
限制網頁/檔案的下載速度
將每個 request 下載的前 512KB 設定為不限速
超過 512KB 後的資料,降速為 256KB/s
這樣設定,對於需要播放、下載影片,可加快的首屏顯示速度
server { listen 80; server_name www.xxx.tw; root /var/www/html; :: location / { #任何的request,下載前 512KB 不限速, 超過後 降速為 256KB/s limit_rate_after 512k; limit_rate 256k; } }
限制 Client 同時間的 request 數目
避免同時間被大量 httpd request
要設定兩個檔案
/etc/nginx.conf http { #限制一個 IP 同時的 request 的數量 # $binary_remote_addr 指 client IP # # zone=mylimit:10m 命名為 mylimit limit_req_zone $binary_remote_addr zone=mylimit:10m rate=100r/m; # 10M : 只以 10MB 記憶體來存每個 IP 的請求次數 # rate=100r/m :每分鐘一百個請求。 # 特別注意: # 並不是 一分鐘執行前100request 後,第 101、102..就失敗 # nginx 會幫你換算成多少毫秒可以接受一個新請求, # 此例是 60/100 = 0.6秒, # 也就是 每0.6秒 nginx 只會幫你執行 1個 requst # 用戶若 0.6秒內同時發了10個 request請求, # 只有第1個會被執行,其它9個都會直接被拒絕掉(503) :: }
關於儲存 IP 記憶體的數量
官方的說明:
The variable’s size is always 4 bytes for IPv4 addresses or 16 bytes for IPv6 addresses. The stored state always occupies 64 bytes on 32-bit platforms and 128 bytes on 64-bit platforms. One megabyte zone can keep about 16 thousand 64-byte states or about 8 thousand 128-byte states
大概就是: 在 64位元的系統,1MB 可以儲存 8000個 IP
網站的設定
www.xxx.tw.conf server { listen 80; server_name www.xxx.tw; root /var/www/html' location / { #限制request速率 limit_req zone=mylimit; ::: } ::: } 當有client碰觸此規則時, nginx error記錄檔中,會有這樣的紀錄: 2024/11/14 13:33:45 [error] 6744#6744: *40250833 limiting requests, excess: 0.874 by zone "mylimit", client: 150.116.97.xxx, server: www.xxx.tw, request: "GET / HTTP/1.0", host: "www.xxx.tw"
$ ab -n 100 -c 3 http://hello3.xxx.tw/ 每次發出 3 requests,總共發出 100 requests :: Server Software: nginx Server Hostname: hello3.xxx.tw Server Port: 80 Document Path: / Document Length: 11 bytes Concurrency Level: 3 Time taken for tests: 2.672 seconds Complete requests: 100 Failed requests: 95 (Connect: 0, Receive: 0, Length: 95, Exceptions: 0) Non-2xx responses: 95 **大量的 request 失敗 Total transferred: 34895 bytes HTML transferred: 18105 bytes Requests per second: 37.43 [#/sec] (mean) Time per request: 80.149 [ms] (mean) Time per request: 26.716 [ms] (mean, across all concurrent requests) Transfer rate: 12.76 [Kbytes/sec] received
nginx log
150.116.x.x - - [14/Nov/2024:14:01:16 +0800] "GET / HTTP/1.0" 200 11 "-" - "ApacheBench/2.3" "-" 150.116.x.x - - [14/Nov/2024:14:01:16 +0800] "GET / HTTP/1.0" 503 190 "-" - "ApacheBench/2.3" "-" 150.116.x.x - - [14/Nov/2024:14:01:16 +0800] "GET / HTTP/1.0" 503 190 "-" - "ApacheBench/2.3" "-" 150.116.x.x - - [14/Nov/2024:14:01:16 +0800] "GET / HTTP/1.0" 503 190 "-" - "ApacheBench/2.3" "-" 150.116.x.x - - [14/Nov/2024:14:01:16 +0800] "GET / HTTP/1.0" 503 190 "-" - "ApacheBench/2.3" "-" 150.116.x.x - - [14/Nov/2024:14:01:16 +0800] "GET / HTTP/1.0" 503 190 "-" - "ApacheBench/2.3" "-" 150.116.x.x - - [14/Nov/2024:14:01:16 +0800] "GET / HTTP/1.0" 503 190 "-" - "ApacheBench/2.3" "-" 150.116.x.x - - [14/Nov/2024:14:01:16 +0800] "GET / HTTP/1.0" 503 190 "-" - "ApacheBench/2.3" "-" 150.116.x.x - - [14/Nov/2024:14:01:16 +0800] "GET / HTTP/1.0" 503 190 "-" - "ApacheBench/2.3" "-" 150.116.x.x - - [14/Nov/2024:14:01:16 +0800] "GET / HTTP/1.0" 503 190 "-" - "ApacheBench/2.3" "-" 150.116.x.x - - [14/Nov/2024:14:01:16 +0800] "GET / HTTP/1.0" 503 190 "-" - "ApacheBench/2.3" "-" 150.116.x.x - - [14/Nov/2024:14:01:16 +0800] "GET / HTTP/1.0" 503 190 "-" - "ApacheBench/2.3" "-" 150.116.x.x - - [14/Nov/2024:14:01:16 +0800] "GET / HTTP/1.0" 503 190 "-" - "ApacheBench/2.3" "-" 150.116.x.x - - [14/Nov/2024:14:01:16 +0800] "GET / HTTP/1.0" 503 190 "-" - "ApacheBench/2.3" "-" 150.116.x.x - - [14/Nov/2024:14:01:16 +0800] "GET / HTTP/1.0" 503 190 "-" - "ApacheBench/2.3" "-" 150.116.x.x - - [14/Nov/2024:14:01:16 +0800] "GET / HTTP/1.0" 503 190 "-" - "ApacheBench/2.3" "-" 150.116.x.x - - [14/Nov/2024:14:01:17 +0800] "GET / HTTP/1.0" 503 190 "-" - "ApacheBench/2.3" "-" 150.116.x.x - - [14/Nov/2024:14:01:17 +0800] "GET / HTTP/1.0" 503 190 "-" - "ApacheBench/2.3" "-" 150.116.x.x - - [14/Nov/2024:14:01:17 +0800] "GET / HTTP/1.0" 503 190 "-" - "ApacheBench/2.3" "-" 150.116.x.x - - [14/Nov/2024:14:01:17 +0800] "GET / HTTP/1.0" 503 190 "-" - "ApacheBench/2.3" "-" 150.116.x.x - - [14/Nov/2024:14:01:17 +0800] "GET / HTTP/1.0" 503 190 "-" - "ApacheBench/2.3" "-" 150.116.x.x - - [14/Nov/2024:14:01:17 +0800] "GET / HTTP/1.0" 503 190 "-" - "ApacheBench/2.3" "-" 150.116.x.x - - [14/Nov/2024:14:01:17 +0800] "GET / HTTP/1.0" 200 11 "-" - "ApacheBench/2.3" "-" 150.116.x.x - - [14/Nov/2024:14:01:17 +0800] "GET / HTTP/1.0" 503 190 "-" - "ApacheBench/2.3" "-" 150.116.x.x - - [14/Nov/2024:14:01:17 +0800] "GET / HTTP/1.0" 503 190 "-" - "ApacheBench/2.3" "-" 150.116.x.x - - [14/Nov/2024:14:01:17 +0800] "GET / HTTP/1.0" 503 190 "-" - "ApacheBench/2.3" "-" 150.116.x.x - - [14/Nov/2024:14:01:17 +0800] "GET / HTTP/1.0" 503 190 "-" - "ApacheBench/2.3" "-" 150.116.x.x - - [14/Nov/2024:14:01:17 +0800] "GET / HTTP/1.0" 503 190 "-" - "ApacheBench/2.3" "-" ::
有關 CDN 的 IP 問題
有一點要注意的是
如果網站是經由 CDN 保護時 (如 Cloudflare )
那 $binary_remote_addr 這個 IP 都會是 CDN 的 IP,
相當於 => 不同的人瀏覽你網站時,都會是 CDN IP
很容易就觸動 limit_req_zone 規則
需要讓 nginx 能記錄 (看到真正的用戶 IP)
以 Cloudflare 為例
新增一個檔案
/etc/nginx/conf.d/nginx-cloudflare-realip.conf
檔案內容:
# Cloudflare IP表: https://www.cloudflare.com/zh-tw/ips/ # 參考 https://github.com/ergin/nginx-cloudflare-real-ip set_real_ip_from 173.245.48.0/20; set_real_ip_from 103.21.244.0/22; set_real_ip_from 103.22.200.0/22; set_real_ip_from 103.31.4.0/22; set_real_ip_from 141.101.64.0/18; set_real_ip_from 108.162.192.0/18; set_real_ip_from 190.93.240.0/20; set_real_ip_from 188.114.96.0/20; set_real_ip_from 197.234.240.0/22; set_real_ip_from 198.41.128.0/17; set_real_ip_from 162.158.0.0/15; set_real_ip_from 104.16.0.0/13; set_real_ip_from 104.24.0.0/14; set_real_ip_from 172.64.0.0/13; set_real_ip_from 131.0.72.0/22; set_real_ip_from 2400:cb00::/32; set_real_ip_from 2606:4700::/32; set_real_ip_from 2803:f800::/32; set_real_ip_from 2405:b500::/32; set_real_ip_from 2405:8100::/32; set_real_ip_from 2a06:98c0::/29; set_real_ip_from 2c0f:f248::/32; # (repeat for all Cloudflare IPs listed at https://www.cloudflare.com/ips/) # use any of the following two real_ip_header CF-Connecting-IP;
重啟 nginx 即可
參考: