WordPress XML-RPC: 3 sposoby wyłączenia & jak rozpoznać ataki na xmlrpc.php | Wyjątek dla Jetpack

Jeśli w logach dostępu witryny na WordPressie widać setki żądań do xmlrpc.php z rzędu z tego samego IP, to zgadywanie haseł wymierzone w WordPress XML-RPC. Sklasyfikowano 8624 wpisy z jednego dnia logów dostępu serwera Nginx + Apache, na którym działa kilka witryn na WordPressie, samodzielnie zastosowano trzy sposoby wyłączenia, sprawdzając kody odpowiedzi, i zebrano, na której warstwie trzeba blokować, aby ograniczyć także uruchamianie PHP.
XML-RPC to protokół, który przez HTTP wymienia dokumenty XML, aby zdalnie wywoływać funkcje WordPressa; od WordPressa 3.5 w ustawieniach nie ma już przełącznika, więc działa włączony od razu po instalacji.
1️⃣ Do czego służy xmlrpc.php i kto z niego korzysta
Od pojawienia się REST API łatwo uznać [xmlrpc.php] za plik, którego nikt już nie używa, ale Jetpack wciąż łączy przez niego witrynę z WordPress.com. Oficjalna dokumentacja Jetpack wprost mówi, że zależy on od połączenia XML-RPC, a na hostingu, który domyślnie blokuje XML-RPC, samo połączenie Jetpack się nie udaje. To jak biuro, które nadal trzyma jeden numer faksu.
W WordPressie 7.1 ten plik przyjmuje 77 metod: 50 z rodziny wp, obsługujących wpisy, media i komentarze, 23 ze starych protokołów blogowych (MetaWeblog, MovableType, Blogger), 2 do pingbacków i 2 testowe metody demo.
Rodzina wp loguje się nazwą użytkownika i hasłem z treści żądania, więc nie przechodzi przez ekran logowania. Pisanie i edycja wpisów, przesyłanie mediów i zarządzanie komentarzami należą do tej rodziny, a 23 metody starych protokołów blogowych pozostawiono, aby wymieniać wpisy tak samo jak inne narzędzia blogowe.
demo.sayHello to wywołanie testowe wbudowane w XML-RPC WordPressa. Po wysłaniu zwraca tylko jedno słowo, [Hello!], a ponieważ nie wymaga logowania, służy do sprawdzania, czy XML-RPC działa. Jego para, demo.addTwoNumbers, dodaje dwie liczby i zwraca wynik.
Wszystkie odpowiadają tylko na treść XML wysłaną metodą POST; jeśli otworzysz adres w przeglądarce, wraca 405 z komunikatem [XML-RPC server accepts POST requests only.].
Pingbacki to też zadanie tego pliku. Po opublikowaniu wpisu pingback trafia do xmlrpc.php witryn, do których linkujesz w treści, a serwer odbiorcy sam otwiera adres nadawcy, by sprawdzić, czy link naprawdę istnieje.
2️⃣ Ataki na xmlrpc.php w logach dostępu
Zgadywanie haseł nie przechodzi przez ekran logowania, tylko wchodzi przez [xmlrpc.php]. W logach dostępu serwera Nginx + Apache, na którym działa kilka witryn na WordPressie, 14 października od 0:00 do 20:00 zarejestrowano 8624 żądania POST /xmlrpc.php z 72 adresów IP, a 19 z nich wysłało dokładnie po 300.
Jedno IP wysłało 300 żądań w 28 minut, od 14:09 do 14:37, a odstęp 5–6 sekund był stały do końca. 8524 żądania przyszły z Jetpack lub WordPress.com w polu User-Agent, a to samo IP na zmianę używało kilku wartości naśladujących prawdziwy format Jetpack, np. [Jetpack by WordPress.com (Jetpack 13.0; WordPress 6.3)].
Tego dnia do 14:00 wyłączenie XML-RPC we wtyczce bezpieczeństwa odsyłało te żądania na stronę główną kodem 302, a po 14:00, po zablokowaniu w Nginx, kończyły się kodem 403. Kod 302 oznacza, że PHP uruchamiało się przy każdym żądaniu, choć treść miała 0 bajtów, a 403 to odpowiedź, która nie przeszła przez PHP.
3️⃣ system.multicall i zgadywanie haseł
Wiele opisów mówi, że [system.multicall] pozwala sprawdzić setki haseł w jednym żądaniu, ale gdy w obrębie żądania logowanie raz się nie powiedzie, WordPress nie porównuje już haseł z pozostałych wywołań. Tylko pierwsze wywołanie przechodzi przez wp_authenticate, a kolejne bez sprawdzania dostają ten sam błąd: [Incorrect username or password.].
Ponieważ na każde wywołanie i tak wraca odpowiedź, jedno żądanie ze 100 błędnymi hasłami dostaje w 0,12 sekundy 100 błędów w odpowiedzi o rozmiarze 23 KB. Jeśli w tym samym żądaniu poprawne hasło stoi dalej, zostaje odrzucone razem z wcześniejszą porażką, a jeśli stoi na początku, udaje się tylko to wywołanie. Hasło porównuje się raz, ale analiza XML i ładowanie WordPressa powtarzają się przy każdym żądaniu, więc obciążenie serwera przy tym ataku wynika z samej liczby żądań, a nie ze sprawdzania logowania.
4️⃣ 3 sposoby wyłączenia XML-RPC i kody odpowiedzi
Nawet po wyłączeniu XML-RPC we wtyczce żądania do [xmlrpc.php] docierają do PHP. Przy włączonym filtrze żądanie ze 100 wywołaniami i tak zwróciło HTTP 200 i 100 błędów (24 KB). Zależnie od warstwy, która odrzuca żądanie, są trzy metody i każda daje inny kod odpowiedzi.
1. Wyłączenie w kodzie filtrem xmlrpc_enabled
Wystarczy dodać w functions.php motywu jedną linię add_filter, która ustawia filtr [xmlrpc_enabled] na false. Wywołania wymagające logowania są wtedy odrzucane z błędem [XML-RPC services are disabled on this site.] i HTTP 405, ale wywołanie testowe demo.sayHello, które nie wymaga logowania, nadal odpowiada kodem 200.
2. Wyłączenie w ustawieniach wtyczki: Perfmatters · Hide My WP
To metoda wyłączenia ustawieniem Disable XML-RPC we wtyczkach takich jak Perfmatters czy Hide My WP. Hide My WP działa tym samym filtrem co wyżej, więc odpowiedzi też są takie same, natomiast Perfmatters oprócz filtra kończy bezpośrednie żądania do [xmlrpc.php] kodem 403 i usuwa jeszcze nagłówek oraz link pingback, dlatego po wyłączeniu zarówno GET, jak i POST odpowiadają 403. Żeby jednak zwrócić to 403, WordPress uruchamia się aż do etapu init, więc odpowiedź trwa 0,13–0,15 sekundy, tyle samo co przy otwartym pliku, a obciążenie PHP zostaje.
3. Blokada na serwerze WWW: Nginx · Apache
Zarówno Nginx, jak i Apache odrzucają żądanie przed PHP, więc w przeciwieństwie do metod z wtyczką WordPress w ogóle się nie uruchamia.
# Nginx — w bloku server, nad blokiem location obsługującym PHP
location = /xmlrpc.php {
deny all;
}Ten blok location umieszcza się nad blokiem location obsługującym PHP, a Nginx od razu odrzuca żądania dokładnie pasujące do [xmlrpc.php]. Po dodaniu trzeba sprawdzić konfigurację i ją przeładować, aby zaczęła działać.
nginx -tnginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successfulsystemctl reload nginxOd przeładowania GET i POST kończą się kodem 403, nie docierając do PHP; treść odpowiedzi to domyślna strona 403 Nginx o rozmiarze 153 bajtów, a odpowiedź trwa 0,02 sekundy.
# Apache — .htaccess w katalogu głównym witryny (Apache 2.4)
<Files xmlrpc.php>
Require all denied
</Files>Na serwerze z samym Apache wystarczy dodać powyższą dyrektywę Files do .htaccess w katalogu głównym witryny, by działała od razu, a domyślna strona 403 Apache (199 bajtów) wraca w tych samych 0,02 sekundy.
curl -s -o /dev/null -w '%{http_code}' -X POST https://example.com/xmlrpc.php -d '<methodCall><methodName>demo.sayHello</methodName></methodCall>'403Powyższe polecenie wysyła metodą POST wywołanie testowe demo.sayHello i sprawdza kod odpowiedzi; po zablokowaniu na serwerze WWW wraca 403. Jeśli wyślesz je bez opcji [-o /dev/null] i [-w], zobaczysz treść: [Hello!] oznacza, że plik jest otwarty. Witryna wyłączona tylko filtrem odpowiada tak samo, więc żeby zobaczyć 405, trzeba wysłać też wywołanie logowania.
5️⃣ Kiedy nie wyłączać i co zamiast tego
Całkowita blokada na serwerze WWW daje największy efekt, ale nie pasuje do witryn połączonych z WordPress.com przez Jetpack. Jetpack zależy od połączenia XML-RPC, a na hostingu, który domyślnie blokuje XML-RPC, jak WP Engine, połączenie działa dopiero po otwarciu wyjątku zgodnie z instrukcjami Jetpack. To samo dotyczy witryn, które chcą odbierać pingbacki.
Takie witryny zawężają dostęp, dopuszczając tylko listę IP publikowaną przez Jetpack (jetpack.com/ips-v4.txt) i odrzucając resztę. Wystarczy w bloku location Nginx dodać po jednej linii allow na każdy zakres IP i zakończyć poleceniem deny all.
Jeśli nie da się zablokować go całkowicie, pozostaje limit na minutę dla niezalogowanych żądań. Ustawienia są takie same jak w części o limicie żądań w Nginx w artykule Zabezpieczenie wp-admin — liczone są tylko żądania bez ciasteczka logowania, więc nie ma to wpływu na pracę zalogowanego administratora.
🔢 FAQ & polecane treści










ℹ️ Informacja o linkach partnerskich
Treści tej witryny zawierają linki partnerskie. Jeśli odwiedzający kupi produkt lub usługę przez taki link, witryna otrzymuje od sprzedawcy prowizję. Kwota płacona przy tym przez kupującego (podczas promocji spada↓) nie wzrasta. Podane ceny, rabaty i dostępność odnoszą się do chwili pisania i mogą różnić się od stanu faktycznego, dlatego przed zakupem sprawdź je u sprzedawcy. Produkty wybieramy i oceniamy według własnych kryteriów, a prowizja nie wpływa ani na kolejność prezentacji, ani na treść ocen.