WordPress XML-RPC:3 種停用方法 & 如何判別 xmlrpc.php 攻擊 | Jetpack 例外

WordPress 網站的存取紀錄中,如果同一個 IP 接連出現數百次 xmlrpc.php 請求,那就是針對 WordPress XML-RPC 的密碼暴力破解。本文分類了一台經營多個 WordPress 網站的 Nginx + Apache 架構伺服器一天的 8,624 筆存取紀錄,並實際套用三種停用方法、確認回應碼,整理出要在哪一層封鎖,才能連 PHP 的執行都減少。
XML-RPC 是透過 HTTP 收發 XML 文件、遠端呼叫 WordPress 功能的協定。從 WordPress 3.5 起,設定畫面中的開關選項被移除,安裝後就一直維持開啟狀態。
1️⃣ xmlrpc.php 的作用與使用場合
REST API 出現之後,很容易把 [xmlrpc.php] 當成已經沒人使用的檔案,但 Jetpack 至今仍透過這個檔案把網站連接到 WordPress.com。Jetpack 官方文件明確寫著它依賴 XML-RPC 連線,在預設封鎖 XML-RPC 的主機上,Jetpack 的連線本身就會失敗。這就像現在辦公室也還會保留一支傳真號碼一樣。
以 WordPress 7.1 來說,這個檔案接受的方法(method)共有 77 個,包括處理文章、媒體與留言的 wp 系列 50 個、舊部落格協定(MetaWeblog、MovableType、Blogger)23 個、Pingback 2 個,以及測試用的 demo 2 個。
wp 系列會用請求內容中的使用者名稱與密碼登入,因此不經過登入畫面。撰寫與修改文章、上傳媒體、管理留言都屬於這個系列;舊部落格協定的 23 個方法,則是為了用和其他部落格工具相同的方式收發文章而保留下來的。
demo.sayHello 是 WordPress XML-RPC 內建的測試用呼叫。送出後只會回傳一句 [Hello!],因為不需要登入,所以用來確認 XML-RPC 是否運作。與它成對的還有把兩個數相加後回傳的 demo.addTwoNumbers。
這些方法都必須用 POST 送出 XML 內容才會回應;用瀏覽器開啟網址,會收到 [XML-RPC server accepts POST requests only.] 訊息與 405。
Pingback 也是這個檔案負責的工作。發佈文章時,會向內文中所連結網站的 xmlrpc.php 發送 Pingback,接收端的伺服器則會直接開啟傳送端的網址,確認連結是否真的存在。
2️⃣ 從存取紀錄看 xmlrpc.php 攻擊
密碼暴力破解不經過登入畫面,而是從 [xmlrpc.php] 進來。在一台經營多個 WordPress 網站的 Nginx + Apache 架構伺服器上,10 月 14 日 00:00 到 20:00 的存取紀錄中,POST /xmlrpc.php 請求有 8,624 次、IP 有 72 個,其中 19 個 IP 剛好各送了 300 次。
其中一個 IP 從 14:09 到 14:37,在 28 分鐘內送了 300 次,5~6 秒的間隔一直固定到最後。有 8,524 次請求在 User-Agent 中寫著 Jetpack 或 WordPress.com,同一個 IP 會輪流使用多個模仿真實 Jetpack 格式的值,例如 [Jetpack by WordPress.com (Jetpack 13.0; WordPress 6.3)]。
同一天 14:00 以前,安全外掛的 XML-RPC 關閉設定會以 302 把這些請求導回首頁;14:00 以後改在 Nginx 封鎖,就以 403 結束。302 代表即使回應內容是 0 位元組,每個請求仍執行了 PHP;403 則是不經過 PHP 的回應。
3️⃣ system.multicall 與密碼暴力破解
很多說明都說 [system.multicall] 能在一次請求中測試數百個密碼,但在 WordPress 中,只要同一個請求裡的登入失敗一次,其餘呼叫的密碼就不會再比對。只有第一個呼叫會經過 wp_authenticate,之後的呼叫不經檢查,直接以相同的錯誤 [Incorrect username or password.] 回應。
每個呼叫仍會各自回傳回應,所以一個裝了 100 個錯誤密碼的請求,會在 0.12 秒內收到 23KB 的回應、其中有 100 個錯誤。在同一個請求中,如果把正確密碼放在後面,會因前面呼叫的失敗而一起被拒絕;放在前面的話,只有那個呼叫會成功。密碼只比對一次,但 XML 解析與 WordPress 載入在每個請求中都會重複,因此這種攻擊的伺服器負擔來自請求數量本身,而不是登入檢查。
4️⃣ 3 種停用 XML-RPC 的方法與回應碼
即使在外掛中關閉 XML-RPC,[xmlrpc.php] 請求仍會進到 PHP。在以篩選器關閉的狀態下送出含 100 個呼叫的請求,仍然原樣收到 HTTP 200 與 100 個錯誤(24KB)。依在哪一層拒絕,方法分為三種,各自的回應碼也不同。
1. 用程式碼以 xmlrpc_enabled 篩選器關閉
只要在佈景主題的 functions.php 中加入一行 add_filter,把 [xmlrpc_enabled] 篩選器設為 false 即可。在這個狀態下,需要登入的呼叫會以 [XML-RPC services are disabled on this site.] 錯誤與 HTTP 405 被拒絕,但不需要登入的測試呼叫 demo.sayHello 仍會以 200 回應。
2. 在外掛設定中關閉:Perfmatters · Hide My WP
這是在 Perfmatters、Hide My WP 等外掛中,用 Disable XML-RPC 設定停用的方法。Hide My WP 和上面一樣是篩選器方式,所以回應也相同;Perfmatters 則是在同一個篩選器之外,還會以 403 結束對 [xmlrpc.php] 的直接請求,並移除 Pingback 標頭與 Pingback 連結,因此關閉後 GET 與 POST 都會回應 403。不過,為了產生這個 403,WordPress 仍會執行到 init 階段,所以回應需要 0.13~0.15 秒,和開啟時的時間相同,PHP 的負擔依然存在。
3. 在網頁伺服器封鎖:Nginx · Apache
無論 Nginx 或 Apache,都會在 PHP 之前拒絕請求,所以和外掛方式不同,WordPress 不會執行。
# Nginx — server 區塊內,放在處理 PHP 的 location 上方
location = /xmlrpc.php {
deny all;
}這個 location 區塊要放在處理 PHP 的 location 上方,Nginx 會直接拒絕與 [xmlrpc.php] 完全相符的請求。加入後,必須先檢查設定再重新載入,才會生效。
nginx -tnginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successfulsystemctl reload nginx重新載入之後,GET 與 POST 都不會到達 PHP,而是以 403 結束;回應內容是 Nginx 預設的 403 頁面,153 位元組,回應時間為 0.02 秒。
# Apache — 網站根目錄的 .htaccess(Apache 2.4)
<Files xmlrpc.php>
Require all denied
</Files>只使用 Apache 的伺服器,把上面的 Files 指令放進網站根目錄的 .htaccess 就會立即生效,同樣在 0.02 秒內回傳 Apache 預設的 403 頁面(199 位元組)。
curl -s -o /dev/null -w '%{http_code}' -X POST https://example.com/xmlrpc.php -d '<methodCall><methodName>demo.sayHello</methodName></methodCall>'403上面的指令會用 POST 送出測試呼叫 demo.sayHello 來確認回應碼,在網頁伺服器封鎖後會回傳 403。拿掉 [-o /dev/null] 與 [-w] 選項再送出,就能看到回應內容:收到 [Hello!] 代表是開啟狀態;不過只用篩選器關閉的網站也會收到相同的回應,所以要連登入呼叫一起送出,才能確認 405。
5️⃣ 不該關閉的情況與替代方案
在網頁伺服器整個封鎖的效果最大,但不適合透過 Jetpack 與 WordPress.com 連接的網站。Jetpack 依賴 XML-RPC 連線,在 WP Engine 這類預設封鎖 XML-RPC 的主機上,必須依照 Jetpack 的說明開放例外才能連線。想接收 Pingback 的網站也一樣。
這類網站可以只允許 Jetpack 公開的 IP 清單(jetpack.com/ips-v4.txt),拒絕其餘請求,藉此縮小範圍。在 Nginx 的 location 區塊中,依 IP 網段逐行加入 allow,最後放上 deny all 即可。
如果無法完全封鎖,還剩下一個方法:對未登入的請求設定每分鐘次數限制。設定方式和 wp-admin 安全 一文中的 Nginx 速率限制項目相同,而且只計算沒有登入 Cookie 的請求,所以不會影響已登入的管理員作業。
🔢 FAQ & 推薦內容










ℹ️ 聯盟行銷說明
本站內容包含聯盟行銷連結。訪客透過這些連結購買商品或服務時,本站會從賣家獲得佣金。在這個過程中,購買者支付的金額(遇到活動折扣時金額會降低↓)並不會因此提高。刊登的價格、折扣與庫存資訊以撰寫當時為準,可能與實際情況不同,購買前請向賣家做最後確認。商品的挑選與評價依本站自訂的標準撰寫,是否支付佣金不會影響介紹順序或評價內容。