Как отключить XML-RPC в WordPress: 3 способа & как распознать атаки на xmlrpc.php | исключение для Jetpack

Если в журнале доступа сайта WordPress с одного IP подряд идут сотни запросов к xmlrpc.php, это подбор паролей через XML-RPC WordPress. На сервере Nginx + Apache с несколькими сайтами WordPress разобраны 8 624 записи журнала доступа за один день, три способа отключения применены на практике и проверены по кодам ответа — и по итогам показано, на каком уровне нужно блокировать, чтобы сократились даже запуски PHP.
XML-RPC — протокол, по которому функции WordPress вызываются удалённо через обмен XML-документами по HTTP. Начиная с WordPress 3.5 переключателя для него на экране настроек нет, поэтому сразу после установки он работает включённым.
1️⃣ Что делает xmlrpc.php и кто его использует
С появлением REST API легко решить, что [xmlrpc.php] — файл, которым больше никто не пользуется, но Jetpack до сих пор связывает через него сайт с WordPress.com. В официальной документации Jetpack прямо сказано, что он зависит от соединения XML-RPC, и на хостингах, где XML-RPC заблокирован по умолчанию, не удаётся даже само подключение Jetpack. Это как в наши дни всё ещё держать в офисе один номер факса.
По состоянию на WordPress 7.1 этот файл принимает 77 методов: 50 из семейства wp, которые работают с записями, медиафайлами и комментариями, 23 из старых протоколов блогов (MetaWeblog, MovableType, Blogger), 2 для пингбэков и 2 тестовых метода demo.
Методы семейства wp выполняют вход по имени пользователя и паролю из тела запроса, поэтому страницу входа они обходят. Создание и редактирование записей, загрузка медиафайлов, управление комментариями — всё это методы этого семейства, а 23 метода старых протоколов блогов оставлены для обмена записями тем же способом, что и в других инструментах для блогов.
demo.sayHello — тестовый вызов, встроенный в XML-RPC WordPress. В ответ он возвращает всего одно слово — [Hello!], а поскольку вход для него не нужен, его используют, чтобы проверить, работает ли XML-RPC. В пару к нему есть demo.addTwoNumbers, который складывает два числа и возвращает сумму.
Все они отвечают, только если отправить тело XML методом POST; если открыть адрес в браузере, вернётся 405 с сообщением [XML-RPC server accepts POST requests only.].
Пингбэки — тоже задача этого файла. При публикации записи пингбэк отправляется на xmlrpc.php сайтов, на которые есть ссылки в тексте, а принимающий сервер сам открывает адрес отправителя и проверяет, действительно ли там есть ссылка.
2️⃣ Атаки на xmlrpc.php в журнале доступа
Подбор паролей идёт не через страницу входа, а через [xmlrpc.php]. В журнале доступа сервера Nginx + Apache с несколькими сайтами WordPress за 14 октября с 0:00 до 20:00 было 8 624 запроса POST /xmlrpc.php с 72 IP-адресов, и 19 из этих IP отправили ровно по 300 запросов.
Один IP отправил 300 запросов за 28 минут, с 14:09 до 14:37, и интервал в 5–6 секунд оставался ровным до самого конца. 8 524 запроса пришли с Jetpack или WordPress.com в User-Agent, причём один и тот же IP чередовал несколько значений, подражающих настоящему формату Jetpack, например [Jetpack by WordPress.com (Jetpack 13.0; WordPress 6.3)].
В тот же день до 14:00 отключение XML-RPC в плагине безопасности отправляло эти запросы на главную с кодом 302, а после 14:00, когда их заблокировали в Nginx, они завершались кодом 403. 302 означает, что PHP запускался на каждый запрос, даже если тело ответа было 0 байт, а 403 — ответ, который до PHP не дошёл.
3️⃣ system.multicall и подбор паролей
Часто пишут, что через [system.multicall] за один запрос перебирают сотни паролей, но если внутри одного запроса вход один раз не удался, WordPress уже не сверяет пароли в остальных вызовах. Через wp_authenticate проходит только первый вызов, а последующие без проверки получают ту же ошибку [Incorrect username or password.].
При этом ответ возвращается на каждый вызов, поэтому на один запрос со 100 неверными паролями за 0,12 секунды приходит ответ размером 23 КБ со 100 ошибками. Если в том же запросе правильный пароль стоит позже, он отклоняется вместе с остальными из-за неудачи предыдущего вызова, а если первым — успешен только этот вызов. Пароль сверяется один раз, но разбор XML и загрузка WordPress повторяются на каждый запрос, так что нагрузку на сервер при этой атаке создаёт само число запросов, а не проверка входа.
4️⃣ 3 способа отключить XML-RPC и коды ответа
Даже если отключить XML-RPC в плагине, запросы к [xmlrpc.php] доходят до PHP. При отключении фильтром на запрос со 100 вызовами всё равно пришли HTTP 200 и 100 ошибок (24 КБ). В зависимости от того, на каком уровне запрос отклоняется, способов три, и коды ответа у них разные.
1. Отключение в коде: фильтр xmlrpc_enabled
Достаточно добавить в functions.php темы одну строку add_filter, которая возвращает false для фильтра [xmlrpc_enabled]. В этом состоянии вызовы, требующие входа, отклоняются с ошибкой [XML-RPC services are disabled on this site.] и HTTP 405, но тестовый вызов demo.sayHello, которому вход не нужен, по-прежнему отвечает 200.
2. Отключение в настройках плагина: Perfmatters·Hide My WP
Это способ отключения через настройку Disable XML-RPC в таких плагинах, как Perfmatters и Hide My WP. Hide My WP использует тот же фильтр, что и выше, поэтому и ответы у него те же, а Perfmatters вдобавок к фильтру завершает прямые запросы к [xmlrpc.php] кодом 403 и удаляет заголовок и ссылку пингбэка, так что после отключения и GET, и POST получают 403. Однако чтобы выдать этот 403, WordPress выполняется вплоть до этапа init, поэтому ответ занимает те же 0,13–0,15 секунды, что и при открытом XML-RPC, и нагрузка на PHP остаётся.
3. Блокировка на веб-сервере: Nginx·Apache
И Nginx, и Apache отклоняют запрос ещё до PHP, поэтому, в отличие от способов с плагином, WordPress не запускается.
# Nginx — внутри блока server, выше location для PHP
location = /xmlrpc.php {
deny all;
}Этот блок location ставится выше location, который обрабатывает PHP, и 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 завершаются кодом 403, не доходя до PHP; тело ответа — стандартная страница 403 Nginx размером 153 байта, а ответ приходит за 0,02 секунды.
# Apache — .htaccess в корне сайта (Apache 2.4)
<Files xmlrpc.php>
Require all denied
</Files>На сервере только с Apache достаточно добавить приведённую выше директиву Files в .htaccess в корне сайта — она применяется сразу, и стандартная страница 403 Apache (199 байт) возвращается за те же 0,02 секунды.
curl -s -o /dev/null -w '%{http_code}' -X POST https://example.com/xmlrpc.php -d '<methodCall><methodName>demo.sayHello</methodName></methodCall>'403Команда выше отправляет тестовый вызов demo.sayHello методом POST и проверяет код ответа; после блокировки на веб-сервере возвращается 403. Если убрать опции [-o /dev/null] и [-w], будет видно тело ответа: [Hello!] означает, что XML-RPC открыт, но сайт, где он отключён только фильтром, отвечает так же, поэтому, чтобы увидеть 405, нужно отправить ещё и вызов со входом.
5️⃣ Когда отключать нельзя и что делать вместо этого
Полная блокировка на веб-сервере даёт наибольший эффект, но не подходит сайтам, подключённым к WordPress.com через Jetpack. Jetpack зависит от соединения XML-RPC, и на хостингах, где XML-RPC заблокирован по умолчанию, как у WP Engine, подключиться можно, только открыв исключение по инструкции Jetpack. То же касается сайтов, которые хотят принимать пингбэки.
Такие сайты сужают доступ: разрешают только список IP, который публикует Jetpack (jetpack.com/ips-v4.txt), а остальные запросы отклоняют. Достаточно добавить в блок location Nginx по строке allow на каждый диапазон IP и в конце поставить deny all.
Если полностью закрыть XML-RPC нельзя, остаётся ограничить число запросов без входа в минуту. Настройка та же, что в разделе об ограничении частоты в Nginx статьи Защита wp-admin — учитываются только запросы без cookie входа, поэтому на работу вошедшего администратора это не влияет.
🔢 FAQ & рекомендуемые материалы










ℹ️ О партнёрских ссылках
Материалы этого сайта содержат партнёрские ссылки. Если посетитель покупает товар или услугу по такой ссылке, сайт получает комиссию от продавца. При этом сумма, которую платит покупатель (при акционных скидках сумма снижается ↓), не увеличивается. Указанные цены, скидки и наличие актуальны на момент написания и могут отличаться, поэтому перед покупкой уточняйте их у продавца. Товары выбираются и оцениваются по нашим собственным критериям, а комиссия не влияет ни на порядок, ни на содержание обзоров.