WordPress 網站被攻擊?讓 Fail2Ban 來救援!

如果您曾經負責做 WordPress 網站,您大概知道當有人決定用垃圾流量塞滿您的伺服器時有多麻煩。像 Cloudflare 這樣的服務雖然很有幫助,且免費方案允許加入速率限制(Rate limiting)規則,但不是每一個網站都可以掛在 Cloudflare。因此,對於那些沒有使用 Cloudflare 的網站,您需要在自己的伺服器上安裝防火牆,而 Fail2Ban 就是一個簡單且效果不錯的的工具。

運作邏輯如下:首先,Nginx 會監控每秒請求數的激增情況。我們會排除 CSS、JS 和media的圖片。接著,任何超過限制的請求都會在 Nginx 的 Access Log 中被記錄為 429 錯誤。Fail2Ban 隨後會掃描這些log尋找特定模式,並封鎖濫用的IP。

設定 Nginx

首先,我們需要建立一個新的速率限制規則,這可以透過把這個程式碼加入 server block 中來完成:

geo $limit_bypass {
    default 0;

    # 任何您想要排除的 IP
    # 我通常會把像 Google 或 Ahrefs 這樣的「好」crawler放在這裡
}

map $request_uri $limited {
    default         $binary_remote_addr;
    ~^/wp-content/  "";
    ~^/wp-includes/ "";
    ~^/wp-admin/ "";
    ~^/wp-json/ "";
}

map $limit_bypass:$limited $final_limit_key {
    ~^1:       "";
    default    $limited;
}

limit_req_zone $final_limit_key zone=one:10m rate=3r/s

上面的程式碼會將 wp-admin、wp-json、wp-content 和 wp-includes 排除在速率限制之外,因為這endpoint包含靜態資源、media 和 AJAX 請求。此外,封鎖 wp-json 或 wp-admin 可能會導致 WP-Admin 後台或某些外掛出現問題。

接著,在您的 server block 中的某個位置,您必須加我們建立的規則:

location / {
  index index.php index.html;
  try_files $uri $uri/ /index.php?$args;

  # 套用速率限制規則:
  limit_req zone=one burst=3 nodelay;
  limit_req_status 429;
}

在 Fail2Ban 中設定 Jail

首先,如果您還沒安裝 Fail2Ban,請先安裝。在 Ubuntu/Debian 中,可以透過 apt 安裝:

sudo apt install fail2ban

就像我前面提到的,Fail2Ban 的運作方式是監控access log並封鎖符合特定模式的 IP。我們先從建立自己的規則開始,複製預設設定檔:

sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local

現在開啟 .local 檔案並定義您的規則,如下所示:

[nginx-req-limit]
ignoreip = <任何您想排除的 IP>
enabled = true
filter = nginx-custom-filter
logpath = /var/log/nginx/access.log
maxretry = 10
findtime = 3600
bantime  = 7200
port     = http,https

這些數字要看您的需求來決定,因為每個網站的流量都不一樣。概念很簡單:如果一個 IP 訪問您網站的次數超過了我們在 Nginx 中定義的限制,它將被封鎖兩小時(7200 秒)。您可以將其修改得更嚴格或更輕鬆。

儲存檔案後,在 /etc/fail2ban/filter.d/nginx-custom-filter.conf 建立filter:

[Definition]
failregex = ^<HOST> - 429 -
ignoreregex =

如您所見,為了讓它正常運作,我們的access log必須符合特定格式。在下一步中,我們將調整 Nginx 的access log格式。

儲存檔案後,重新啟動 Fail2Ban:

sudo service fail2ban restart

檢查 Jail 是否啟用:

sudo fail2ban-client status

調整 Nginx access log格式

Nginx 的預設格式並非我們所需的,因此我們必須建立一個客製化的access log格式。

要使用自定客製化的access log格式,請在 access_log 指令的末尾加上格式名稱(可以是任何名稱),如下所示:

access_log /var/log/nginx/access.log custom

接著,在您的 nginx.conf 中加 access log 格式(需要在 http 區塊內):

log_format custom '$remote_addr - $status - $remote_user [$time_local] '
                  '"$request" $body_bytes_sent '
                  '"$http_referer" "$http_user_agent" "$gzip_ratio"';

重新啟動 Nginx:

sudo nginx -s reload

現在 Fail2Ban 將會監控access log,並對access log中出現的任何 “429” 回應代碼做出反應。

測試設定

您可以使用一個簡單的bash script,透過重複請求您的伺服器:

for i in {1..200}; do curl -s https://example.com >/dev/null; done

然後檢查 Fail2Ban:

sudo fail2ban-client status nginx-http

您應該會看到您的 IP 出現在封鎖名單上。然後你使者在瀏覽器上打開你的網站,應該會失敗(通常會一直轉圈圈)。

如何用 Cloudflare 設定Rate Limiting

您也可以透過前往 Security(安全性) → Security Rules(安全性規則) → Rate Limiting Rules(速率限制規則) 在 Cloudflare 中建立類似的邏輯。建立一個新規則並輸入此程式碼:

(not starts_with(http.request.uri.path, "/wp-content/") and not starts_with(http.request.uri.path, "/wp-includes/") and not starts_with(http.request.uri.path, "/wp-json/") and not starts_with(http.request.uri.path, "/wp-admin/"))

接著將速率限制設定為每 10 秒 8–10 個請求(時間只能設定為 10 秒)。

對於動作(Action),選擇「Block(封鎖)」——— 請注意,持續時間也只能設定為 10 秒。

Cloudflare 的選項限制較多,但即使是免費方案也提供速率限制功能,如果您不想在自己的伺服器上進行手動設定,這是一個不錯的替代工具。