Como bloquear ataques de força bruta no xmlrpc.php do WordPress
Última atualização:
O xmlrpc.php é a antiga API remota do WordPress. Poucos sites ainda precisam dele, e todo site WordPress na internet é atacado por ele. O método system.multicall executa muitas chamadas numa única requisição, então um só POST pode testar centenas de senhas, e é por isso que os limitadores de login que contam as requisições ao wp-login.php não o enxergam. O método pingback.ping é usado para fazer o seu site enviar requisições para o site de outra pessoa.
Abaixo: como reconhecer o ataque nos logs de acesso, como bloquear o xmlrpc.php no nginx, no Apache ou no próprio WordPress, o que fazer se você usa o Jetpack, e como limitar a taxa ou banir com fail2ban quando ele precisa continuar aberto. Os comandos são para Debian e Ubuntu e para a família RHEL; example.com representa o seu site.
Reconheça um ataque XML-RPC nos logs de acesso
Conte as requisições POST ao xmlrpc.php por endereço. Um site normal recebe poucas por dia, do Jetpack ou de um app. Centenas vindas de um endereço, ou um fluxo constante vindo de muitos, é um ataque.
sudo sh -c "grep -hI 'POST /xmlrpc.php' /var/log/nginx/*access*.log /var/log/apache2/*access*.log \
/var/log/httpd/*access_log /usr/local/apache/domlogs/* /var/log/apache2/domlogs/* 2>/dev/null" \
| awk '{print $1}' | sort | uniq -c | sort -rn | head -20O comando lê os lugares de sempre: nginx, Apache no Debian e no Ubuntu, httpd na família RHEL e os logs por domínio do cPanel. As requisições por hora mostram quando começou:
sudo sh -c "grep -hI 'POST /xmlrpc.php' /var/log/nginx/access.log /var/log/apache2/access.log /var/log/httpd/access_log 2>/dev/null" \
| awk '{print substr($4, 2, 14)}' | uniq -c | tail -n 24- O status é
200mesmo com senha errada: o XML-RPC informa a falha dentro da resposta. Não leia200como um login bem-sucedido. - Respostas grandes, repetidas por um endereço a cada poucos segundos, costumam ser
system.multicall: uma resposta para muitas tentativas de senha. - O log não mostra qual método foi chamado: o corpo da requisição não é registrado. Força bruta e abuso de pingback parecem iguais aqui.
Para saber se o XML-RPC responde num site, peça a lista de métodos de qualquer máquina. Uma lista de métodos quer dizer que está ativo; um 403 quer dizer que está bloqueado.
curl -s -X POST -H 'Content-Type: text/xml' \
-d '<?xml version="1.0"?><methodCall><methodName>system.listMethods</methodName></methodCall>' \
https://example.com/xmlrpc.php | head -c 400; echoBloqueie o xmlrpc.php no nginx ou no Apache
Bloquear no servidor web é a opção mais barata: a requisição é recusada antes de o PHP e o WordPress carregarem. No nginx, adicione isto ao bloco server de cada site WordPress:
# In the site's server { } block, before the "location ~ \.php$" block
location = /xmlrpc.php {
deny all;
}sudo nginx -t && sudo systemctl reload nginxNo Apache 2.4, coloque isto no .htaccess do site (não precisa de reload; o LiteSpeed também o lê) ou no <VirtualHost> dele:
# In the site's .htaccess or <VirtualHost>
<Files "xmlrpc.php">
Require all denied
</Files>sudo apachectl configtest && sudo systemctl reload apache2sudo apachectl configtest && sudo systemctl reload httpdAtenção: Bloquear o xmlrpc.php quebra o que ainda depende dele: o Jetpack (veja abaixo), os pingbacks e alguns apps e ferramentas de publicação mais antigos. Se não tiver certeza, confira antes nos logs do passo 1 se há chamadas legítimas.
Desative o XML-RPC dentro do WordPress
Onde você não pode mudar a configuração do servidor web, um plugin must-use faz isso pelo WordPress. Plugins must-use carregam em toda requisição e não podem ser desativados por engano no painel de administração. Troque o caminho pelo do seu site:
sudo mkdir -p /var/www/example.com/wp-content/mu-plugins
sudo tee /var/www/example.com/wp-content/mu-plugins/disable-xmlrpc.php > /dev/null <<'EOF'
<?php
// Turns off the XML-RPC methods that need a login, and pingbacks.
add_filter( 'xmlrpc_enabled', '__return_false' );
add_filter( 'xmlrpc_methods', function ( $methods ) {
unset( $methods['pingback.ping'], $methods['pingback.extensions.getPingbacks'] );
return $methods;
} );
EOFNota: O filtro xmlrpc_enabled só desativa os métodos que exigem login, como diz a referência do WordPress; o segundo filtro remove os métodos de pingback. O PHP continua rodando a cada requisição, então, sob um ataque pesado, o bloqueio no servidor web acima é mais leve.
Plugins fazem o mesmo: plugins dedicados do tipo "Disable XML-RPC", e plugins de segurança como Wordfence e Solid Security têm uma opção para isso (em novembro de 2026). Um plugin é fácil de desativar sem querer durante uma atualização; o mu-plugin e a regra do servidor web não.
Mantenha o Jetpack funcionando
O Jetpack conversa com o seu site pelo xmlrpc.php, então bloqueá-lo para todo mundo derruba a conexão do Jetpack. O Jetpack publica as faixas de endereços dos seus servidores na página de allowlist: libere essas e bloqueie o resto. No nginx, o location também precisa passar a requisição para o PHP:
location = /xmlrpc.php {
# Jetpack's ranges, from its allowlist page (check it for changes)
allow 122.248.245.244/32;
allow 54.217.201.243/32;
allow 54.232.116.4/32;
allow 192.0.64.0/18;
allow 195.234.108.0/22;
deny all;
# then the same lines as in your "location ~ \.php$" block, for example:
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_pass unix:/run/php/php-fpm.sock;
}Copie as linhas de PHP do próprio bloco location ~ \.php$ do site: o caminho do socket muda de um sistema para outro. No Apache, Require ip sozinho libera só essas faixas:
<Files "xmlrpc.php">
Require ip 122.248.245.244/32 54.217.201.243/32 54.232.116.4/32 192.0.64.0/18 195.234.108.0/22
</Files>Nota: Faixas como o Jetpack as listava em novembro de 2026. Elas podem mudar: confira a página de vez em quando. Atrás do Cloudflare ou de outro proxy, essas regras veem o endereço do proxy, a menos que o endereço real do visitante seja restaurado antes (real_ip no nginx, mod_remoteip no Apache).
Limite a taxa do xmlrpc.php quando ele precisa ficar aberto
Se um app ou serviço que você não consegue listar por endereço precisa do XML-RPC, limite quantas vezes cada endereço pode chamá-lo. No nginx, defina uma zona no contexto http e use-a para o xmlrpc.php: aqui, 6 requisições por minuto por endereço, com um burst de 5, e 429 acima disso.
# /etc/nginx/conf.d/xmlrpc-limit.conf (read inside the http { } block)
limit_req_zone $binary_remote_addr zone=xmlrpc:10m rate=6r/m;# In the site's server { } block
location = /xmlrpc.php {
limit_req zone=xmlrpc burst=5 nodelay;
limit_req_status 429;
# then the same lines as in your "location ~ \.php$" block, for example:
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_pass unix:/run/php/php-fpm.sock;
}sudo nginx -t && sudo systemctl reload nginxNota: Um limite de taxa conta requisições, não senhas: uma requisição system.multicall dentro do limite ainda pode levar centenas de tentativas. Combine com senhas fortes, ou bana os endereços com fail2ban. O Apache não tem um equivalente embutido; use o fail2ban abaixo.
Bana atacantes do XML-RPC com fail2ban
O fail2ban lê o log de acesso e bane um endereço no firewall, então funciona tanto com nginx quanto com Apache. Instale-o como em Como parar ataques de força bruta no SSH (repositórios do Debian e do Ubuntu, EPEL na família RHEL). Depois crie um filtro para as requisições POST ao xmlrpc.php:
sudo tee /etc/fail2ban/filter.d/wordpress-xmlrpc.conf > /dev/null <<'EOF'
[Definition]
failregex = ^<HOST> \S+ \S+ \[[^]]+\] "POST /xmlrpc\.php
ignoreregex =
EOFE um jail: 5 requisições em 60 segundos, banimento de uma hora. Coloque o seu próprio endereço em ignoreip e, se você usa o Jetpack, as faixas dele. Para o Apache, defina logpath como /var/log/apache2/*access.log no Debian e no Ubuntu ou /var/log/httpd/*access_log na família RHEL.
sudo tee /etc/fail2ban/jail.d/wordpress-xmlrpc.local > /dev/null <<'EOF'
[wordpress-xmlrpc]
enabled = true
port = http,https
filter = wordpress-xmlrpc
logpath = /var/log/nginx/access.log
maxretry = 5
findtime = 60
bantime = 3600
ignoreip = 127.0.0.1/8 ::1 192.0.2.10
EOFTeste o filtro no log real antes de ativá-lo: o fail2ban-regex mostra quantas linhas casam. Depois reinicie o fail2ban e confira o jail.
sudo fail2ban-regex /var/log/nginx/access.log /etc/fail2ban/filter.d/wordpress-xmlrpc.conf
sudo systemctl restart fail2ban
sudo fail2ban-client status wordpress-xmlrpcAtenção: Atrás do Cloudflare ou de outro proxy, o log de acesso mostra o endereço do proxy: o fail2ban baniria o proxy e deixaria o site fora do ar para muitos visitantes. Restaure antes o endereço real no log, ou bloqueie no proxy.
Como o InfraFence lida com ataques XML-RPC
O firewall web do InfraFence lê os logs de acesso do nginx, Apache e LiteSpeed e, a partir do agente 1.0.42, também do Traefik, Caddy, HAProxy e de servidores web em contêineres. Atrás do Cloudflare, ele pega o endereço do visitante do CF-Connecting-IP, e só das faixas do próprio Cloudflare.
- Cada 5 requisições POST ao
xmlrpc.phpde um endereço num site, dentro de um minuto, somam 25 pontos ao score desse endereço. Um ataque contínuo continua somando. - O score também junta os outros ataques do endereço, como força bruta no
wp-login.php, varredura de plugins ou tentativas de exploit, e cai 5 pontos por minuto, então uma requisição estranha isolada some. - Com 80 pontos, o endereço é banido no firewall e o evento é reportado como crítico. Limites e pontos podem ser alterados por servidor, e o modo monitor reporta sem banir.
Veja Firewall web para as regras e o score. O que ele não faz: conta requisições, então não vê quantas senhas uma requisição system.multicall carrega; não altera a configuração do seu nginx, Apache ou WordPress, então o xmlrpc.php fica aberto até você bloqueá-lo como acima; e não tem exceção embutida para o Jetpack: se os servidores do Jetpack chamarem com frequência suficiente para pontuar, adicione as faixas deles à whitelist. O banimento vem do log, então a requisição que o disparou já foi respondida.
Perguntas
- É seguro desativar o xmlrpc.php?
- Para a maioria dos sites, sim: o editor de blocos e o painel de administração usam a API REST, não o XML-RPC. Confira antes se você usa o Jetpack, pingbacks, um app ou uma ferramenta de publicação que ainda dependa dele.
- Por que os ataques aparecem com status 200?
- O XML-RPC responde
200e informa o login com falha dentro da resposta. O código de status não diz nada sobre se uma senha funcionou. - Meu plugin limitador de login não segura o ataque. Por quê?
- Muitos limitadores contam as tentativas no
wp-login.php. Uma requisiçãosystem.multicallpeloxmlrpc.phpleva centenas de tentativas numa só requisição e passa por fora deles. Bloqueie ou limite o próprioxmlrpc.php.
Teste grátis em 1 servidor
O Starter é gratuito para um servidor, com proteção completa e bloqueio automático. Sem cartão de crédito.
Instale o agente como root, com o seu token no lugar de <TOKEN>:
curl -fsSL https://github.com/infrafence/infrafence-agent/releases/latest/download/install.sh | sudo bash -s -- --token <TOKEN>O token é criado no painel, em Servidores › Adicionar servidor.
