你的 WordPress 後台正在被人嘗試登入,而且就在此刻。打開伺服器的存取紀錄(access log)你會看到一個事實:陌生 IP 不斷對 wp-login.php 發出 POST 請求,一天幾百次到上萬次都有。WordPress 防暴力破解之所以是每個站長都該處理的第一道功課,原因很現實——預設安裝完全不限制登入次數,攻擊者可以拿外洩密碼字典無限次嘗試,直到猜中為止。
問題不在「會不會被攻擊」,而在「被猜中之前你擋下了沒有」。這篇文章把防護拆成兩條主線:一是登入限流(限制單一 IP 的登入嘗試次數並暫時鎖定),二是IP 封鎖(在更底層直接讓攻擊流量進不來)。前者多數靠外掛就能做,後者的進階做法是把伺服器層的 fail2ban 接進 WordPress 的登入失敗事件。下面會把外掛方案、fail2ban 整合、設定參數、以及大家最常踩的真實環境陷阱一次講清楚,並幫你判斷自己的主機環境該選哪一條路。
為什麼 WordPress 預設這麼容易被暴力破解
預設的 WordPress 對登入嘗試完全不設防,這是它容易被打的根本原因。一套全新安裝的 WordPress 有三個對攻擊者非常友善的特性:登入次數無上限、嘗試之間沒有任何延遲、失敗到一定次數也不會鎖定或封鎖 IP。換句話說,自動化工具可以毫無阻力地一秒測好幾組帳密。
攻擊者鎖定的不只是登入頁本身。常見的暴力破解管道有兩個:
- wp-login.php:所有 WordPress 站台的預設登入頁,位置全世界都知道,機器人每天掃遍整個網路找弱密碼帳號。
- xmlrpc.php:一個老舊的遠端呼叫介面,原本設計給手機 App、Pingback、Trackback 使用。它的危險在於
system.multicall功能可以把多組帳密包進「一次」請求送出,等於用遠少於逐一嘗試的請求數測完上百組密碼,效率遠高於直接打登入頁。多數網站根本用不到它,卻預設開啟。
理解攻擊者想達成什麼,才知道該防哪裡。暴力破解通常服務於幾種目的:純粹猜密碼登入後台、用其他網站外洩的帳密組合做「撞庫」(credential stuffing)、或單純用海量登入請求把登入頁灌爆造成類似阻斷服務的效果。光靠「設一組強密碼」只能降低被猜中的機率,無法阻止機器人持續消耗你的伺服器資源、也擋不住撞庫,所以才需要主動的限流與封鎖機制。
登入限流要怎麼用外掛做
對沒有伺服器操作權限的站長來說,登入限流外掛是最快上手的方案:裝好、設定鎖定門檻、啟用,幾分鐘內就有基本防護。它的運作邏輯是記錄每個 IP 的失敗次數,超過門檻就在一段時間內拒絕該 IP 再嘗試登入。
目前流通度最高的幾款各有定位:
- Limit Login Attempts Reloaded:專注做登入限流,安裝量在同類外掛裡名列前茅,介面單純,適合只想解決「限制嘗試次數」這一件事的站長。
- Wordfence:綜合型資安外掛,限流只是其中一塊,另含防火牆、惡意程式掃描、弱密碼偵測。功能多但相對吃資源。
- Solid Security:免費版就含弱密碼偵測與密碼政策強制(可要求最少字元數、混合大小寫數字符號、定期更換、禁止重複使用舊密碼),把「限流」與「從源頭杜絕弱密碼」一起處理。
設定外掛時有幾個欄位要拿捏。最大嘗試次數決定失敗幾次後鎖定,太鬆形同虛設、太緊容易誤鎖自己;鎖定時長決定鎖多久,可以設成「累犯延長」,每次再犯就拉長鎖定時間,逼退鍥而不捨的機器人。若你同時經營多個站台,部分外掛支援同步鎖定,把一個站偵測到的惡意 IP 分享給其他站,少打一個就少一分風險。
需要留意的是,外掛方案有兩個先天限制。第一,外掛本身也是攻擊面——過去 Limit Login Attempts 系列外掛就被揭露過漏洞,所以這類外掛同樣要保持更新。第二,限流發生在 PHP 層,請求其實已經打進 WordPress 才被擋下,伺服器仍要為每一次惡意嘗試耗用資源。當攻擊量大到一定程度,把防線往伺服器更外層推、在請求碰到 PHP 之前就攔掉,效果會明顯許多——這正是 fail2ban 登場的時機。
fail2ban 是什麼,為什麼比外掛更底層
fail2ban 是 Linux 伺服器上的一支防護程式,它不認識 WordPress,只做一件事:持續監看指定的紀錄檔(log),用正規表示式(regular expression)比對出「失敗事件」,當同一個 IP 在設定的時間窗內失敗達到門檻,就呼叫系統防火牆(多為 iptables)把這個 IP 直接封掉。被封的 IP 連 TCP 連線都建立不了,請求根本到不了 PHP,伺服器自然省下大量運算。
它和登入限流外掛最大的差異在攔截的「位置」。外掛在 WordPress 內部判斷與拒絕,fail2ban 在作業系統的防火牆層判斷與拒絕。位置越外層,能擋下的攻擊型態越廣、消耗的資源越少。fail2ban 出廠就內建一批常見服務的過濾規則(從 SSH 到 Apache、Nginx),WordPress 的部分需要自己補上一條規則,告訴它「登入失敗長什麼樣子」。
要讓 fail2ban 看得到 WordPress 的登入失敗,有兩種主流串接方式,差別在「把失敗事件寫到哪裡」:
- 走存取紀錄(access log):裝一支極小的外掛,讓 WordPress 在登入失敗時回傳 HTTP 401 狀態碼。401 會被寫進伺服器的存取紀錄,fail2ban 比對紀錄裡帶 401 的
wp-login.php請求即可。優點是不依賴 PHP 額外寫日誌、設定單純,單一主機的情境最省事。 - 走系統日誌(syslog):使用 WordPress.org 上的 WP fail2ban 外掛,它不靠狀態碼,而是直接把登入失敗事件寫進系統日誌。好處是即使存取紀錄因故讀不到也照樣有紀錄,且天生適合「多站集中監控」;代價是大規模攻擊時系統日誌會被灌爆,其他錯誤訊息會被淹沒,較難排查。
單一站台、單一主機,走存取紀錄的方式最快也最輕。跨多台伺服器或多站集中管理時,再考慮走系統日誌、把所有失敗事件集中到一個位置監看。
fail2ban 整合 WordPress 的完整設定步驟
整合分成三步:裝好 fail2ban、寫一條辨識 WordPress 登入失敗的過濾規則(filter)、再寫一條決定如何處置的監牢規則(jail)。共用主機通常無法自行安裝,需先向主機商確認是否提供 fail2ban,以及能否加掛自訂過濾規則;VPS 或獨立主機則可自行操作。
第一步,在 Debian 或 Ubuntu 系統安裝:
sudo apt-get install fail2ban
第二步,在 /etc/fail2ban/filter.d/ 底下新增一個過濾規則檔,例如 wordpress.conf,定義「什麼樣的紀錄算一次失敗」。下面這條同時涵蓋 wp-login.php 與 xmlrpc.php 的失敗請求:
# /etc/fail2ban/filter.d/wordpress.conf
[Definition]
failregex = ^<HOST> .* "(GET|POST) /wp-login.php.*" 401
^<HOST> .* "(GET|POST) /xmlrpc.php.*" 401
ignoreregex =
failregex 是 fail2ban 要抓的失敗特徵,<HOST> 是它用來抓出來源 IP 的佔位符。即使沒有要忽略的項目,ignoreregex = 這行也要留著,否則重啟時 fail2ban 會跳警告。
第三步,在 /etc/fail2ban/jail.local 新增監牢設定,指定要監看哪個紀錄檔、用哪條過濾規則、以及封鎖的判定條件:
[wordpress]
enabled = true
port = http,https
filter = wordpress
logpath = /var/log/nginx/access.log
maxretry = 5
findtime = 600
bantime = 3600
各欄位的意思如下:
- enabled:設為
true才會啟用這條監牢。 - port:監看的連接埠,網站登入走
http,https即可。 - filter:對應前一步建立的過濾規則名稱。
- logpath:要監看的紀錄檔路徑。Nginx 通常是
/var/log/nginx/access.log,Apache 則改成/var/log/apache2/access.log,請依你的伺服器調整。 - maxretry:時間窗內失敗幾次就封鎖。
- findtime:計算失敗次數的時間窗,單位是秒,
600代表 10 分鐘。 - bantime:封鎖時長,單位是秒,
3600代表 1 小時。
改完設定重啟服務即可生效:
sudo systemctl restart fail2ban
若採用走存取紀錄的方式,記得另外放一支讓登入失敗回傳 401 的小外掛,核心邏輯只是掛在 wp_login_failed 事件上設定 401 狀態碼,fail2ban 才有 401 可比對;若改用 WP fail2ban 外掛走系統日誌,則 logpath 要指向系統日誌、過濾規則也換成對應系統日誌格式的版本。
maxretry、findtime、bantime 該怎麼設
這三個參數共同決定「多敏感才封、封多久」,沒有絕對標準,要依站台性質權衡。它們的關係是:在 findtime 這段時間窗內,同一 IP 失敗達到 maxretry 次,就封鎖 bantime 這麼久。
下面是幾組常見的搭配,可當起點再依實際情況微調:
| 取向 | maxretry | findtime | bantime | 適合情境 |
|---|---|---|---|---|
| 出廠預設 | 3 | 600(10 分鐘) | 600(10 分鐘) | 一般部落格,寬鬆好上手 |
| 較嚴格 | 5 | 1200(20 分鐘) | 86400(24 小時) | 一般商業官網 |
| 高敏感 | 3 | 600(10 分鐘) | 86400(24 小時) | 後台帳號少、登入者固定 |
設定時抓住三個原則。maxretry 別壓太低,正常使用者偶爾打錯密碼、重打一兩次很常見,門檻太低會誤鎖真人;多數情況設在 3 到 5 之間。findtime 是觀察窗,越短越只抓「短時間密集嘗試」這種典型機器人行為。bantime 則是嚇阻力道,封 10 分鐘只能擋一時,封 24 小時才足以讓多數自動化工具放棄。
真正棘手的是「累犯」——有些機器人會乖乖等封鎖到期,隔一天再回來試。對付這種,可以把封鎖時長設成累進式,或搭配「累犯偵測」機制:同一個 IP 反覆被封到一定次數後,就改為長期或永久封鎖,不再給它重來的機會。
設定 fail2ban 最容易踩的三個坑
把 fail2ban 接上去只是開始,真正讓人半夜被叫醒的,往往是這幾個環境細節。多數教學只給設定檔卻沒提這些,結果不是封不到攻擊者、就是把自己鎖在門外。
第一個坑是反向代理讓你封錯 IP。 如果站台前面掛了 Cloudflare、負載平衡器或其他反向代理,伺服器紀錄裡的來源 IP 會全部變成代理的 IP,而不是訪客的真實 IP。這時 fail2ban 抓到的「攻擊 IP」其實是代理,封下去要嘛沒效果、要嘛把整個代理擋掉連累所有正常訪客。解法是讓網頁伺服器改記真實 IP(Nginx、Apache 都有對應模組讀取代理傳來的 X-Forwarded-For 標頭),確認紀錄裡寫的是真實來源 IP,fail2ban 才封得對。
第二個坑是把自己鎖在外面。 測試或自己手滑連打錯密碼時,你的 IP 也會被算進失敗次數而遭封鎖。務必把自己的固定 IP(辦公室、家用固定 IP)加進白名單;fail2ban 可在設定裡用 ignoreip 排除特定 IP。若要測試封鎖是否生效,建議改用手機行動網路等「另一條對外 IP」去打錯密碼,別拿日常使用的那條連線測,免得把自己鎖出後台。同理,外掛方案也應把信任 IP 加入白名單。
第三個坑是設了卻沒驗證有沒有在跑。 改完設定檔重啟後,一定要確認監牢真的載入、也真的在封人。用內建的客戶端工具查狀態:
sudo fail2ban-client status
這會列出目前啟用中的所有監牢,確認清單裡有 wordpress。要看單一監牢的細節(包含目前被封的 IP 清單與累計封鎖數),加上監牢名稱:
sudo fail2ban-client status wordpress
萬一誤封了某個該放行的 IP,可以手動解封:
sudo fail2ban-client set wordpress unbanip 1.2.3.4
設定能載入、攻擊 IP 進得了封鎖清單、自己的 IP 不在裡面,這條防線才算真的立起來。
限流與封鎖之外,還要疊上哪些防線
登入限流與 IP 封鎖擋的是「猜密碼」這個動作,但完整的防護是分層的,把攻擊者必須突破的關卡一層層疊高。前面兩道處理「猜的速度」,下面幾道則從「猜得到嗎」「猜中了又如何」的角度補強。
- 隱藏登入頁:把預設的
wp-login.php換成自訂網址(例如用 WPS Hide Login 這類外掛),讓機器人連登入入口都找不到。這不是真正的安全機制,但能大幅減少自動化掃描的命中量,屬於低成本的減噪手段。 - 關閉 XML-RPC:若你沒在用 Jetpack 或 WordPress 手機 App,
xmlrpc.php可以直接關掉,沒有副作用。可裝 Disable XML-RPC 這類外掛,或在伺服器層擋掉對它的請求,順手堵掉前面提到的高效率攻擊管道。 - 雙重驗證(2FA):對管理員等高權限帳號啟用 2FA,常見做法是時間型一次性密碼(TOTP,搭配 Google Authenticator 等驗證器 App)。即使密碼真的被猜中或撞庫命中,少了第二道驗證碼,攻擊者一樣進不去。
- CAPTCHA 或人機驗證:在登入頁加上 reCAPTCHA、hCaptcha 或 Cloudflare Turnstile,過濾掉純自動化的嘗試。雖然進階機器人能破解部分驗證,但對絕大多數低成本的暴力破解工具仍是有效門檻。
- 強密碼政策:用 Solid Security 等外掛強制最低密碼強度與定期更換,從源頭縮小「被字典猜中」的可能。限流再嚴,密碼是
admin123也只是拖時間而已。
該怎麼選,可以照主機環境和站台重要性來排。共用主機、無法操作伺服器的個人站,先把登入限流外掛、隱藏登入頁、關閉 XML-RPC、2FA 這幾項做滿,CP 值最高;有 SSH 權限的 VPS 或商業站,再把 fail2ban 補上,讓惡意流量在碰到 PHP 之前就被防火牆攔掉,省下的伺服器資源在被密集攻擊時特別有感。涉及會員資料或收款的站台,上述全做之外,還要把帳號權限收斂到最小、定期檢查有沒有可疑的管理員帳號,並保持核心、外掛、主題更新到最新版。
沒有任何一招能讓網站百分之百安全,但暴力破解這種最常見、最自動化的攻擊,恰恰是最容易用低成本擋下的。今天就打開存取紀錄看一眼有多少陌生 IP 在敲你的登入頁,先從一道登入限流開始,再依主機環境決定要不要把 fail2ban 疊上去——多疊一層,攻擊者就多一道過不去的牆。