InfraFenceInfraFence

Live Security Operations

Inizia

Come bloccare gli attacchi brute force su xmlrpc.php di WordPress

Ultimo aggiornamento:

xmlrpc.php è la vecchia API remota di WordPress. Pochi siti ne hanno ancora bisogno, e ogni sito WordPress su internet viene attaccato attraverso di essa. Il suo metodo system.multicall esegue molte chiamate in una sola richiesta, quindi un solo POST può provare centinaia di password: per questo i limitatori di login che contano le richieste a wp-login.php non lo vedono. Il metodo pingback.ping viene usato per far inviare al tuo sito richieste verso il sito di qualcun altro.

Qui sotto: come riconoscere l'attacco nei log di accesso, come bloccare xmlrpc.php in nginx, Apache o direttamente in WordPress, cosa fare se usi Jetpack, e come limitarlo o bannare con fail2ban quando deve restare aperto. I comandi sono per Debian e Ubuntu e per la famiglia RHEL; example.com sta per il tuo sito.

Riconoscere un attacco XML-RPC nei log di accesso

Conta le richieste POST a xmlrpc.php per indirizzo. Un sito normale ne vede poche al giorno, da Jetpack o da un'app. Centinaia da un solo indirizzo, o un flusso costante da molti, sono un attacco.

Qualsiasi distribuzione
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 -20

Il comando legge i posti abituali: nginx, Apache su Debian e Ubuntu, httpd sulla famiglia RHEL, e i log per dominio di cPanel. Le richieste per ora mostrano quando è cominciato:

Qualsiasi distribuzione
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
  • Lo stato è 200 anche con una password sbagliata: XML-RPC riporta l'errore dentro la risposta. Non leggere 200 come un login riuscito.
  • Risposte grandi, ripetute dallo stesso indirizzo ogni pochi secondi, sono tipicamente system.multicall: una risposta per molti tentativi di password.
  • Il log non mostra quale metodo è stato chiamato: il corpo della richiesta non viene registrato. Brute force e abuso dei pingback qui si somigliano.

Per sapere se XML-RPC risponde su un sito, chiedigli l'elenco dei metodi da qualsiasi macchina. Un elenco di metodi vuol dire che è attivo; un 403 che è bloccato.

Qualsiasi macchina con curl
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; echo

Bloccare xmlrpc.php in nginx o Apache

Bloccare sul web server è l'opzione più leggera: la richiesta viene rifiutata prima che PHP e WordPress si carichino. In nginx, aggiungi questo al blocco server di ogni sito WordPress:

nginx
# In the site's server { } block, before the "location ~ \.php$" block
location = /xmlrpc.php {
    deny all;
}
Qualsiasi distribuzione
sudo nginx -t && sudo systemctl reload nginx

In Apache 2.4, mettilo nel .htaccess del sito (non serve ricaricare; lo legge anche LiteSpeed) o nel suo <VirtualHost>:

Apache
# In the site's .htaccess or <VirtualHost>
<Files "xmlrpc.php">
    Require all denied
</Files>
Debian / Ubuntu
sudo apachectl configtest && sudo systemctl reload apache2
RHEL, Rocky Linux, AlmaLinux, Oracle Linux, Fedora
sudo apachectl configtest && sudo systemctl reload httpd

Attenzione: Bloccare xmlrpc.php rompe ciò che lo usa ancora: Jetpack (vedi sotto), i pingback e alcune app e strumenti di pubblicazione meno recenti. Se non sei sicuro, controlla prima nei log del passo 1 se ci sono chiamate legittime.

Disattivare XML-RPC dentro WordPress

Dove non puoi cambiare la configurazione del web server, un plugin must-use lo fa da WordPress. I plugin must-use si caricano a ogni richiesta e non si possono disattivare per sbaglio dall'area di amministrazione. Sostituisci il percorso con quello del tuo sito:

Qualsiasi distribuzione
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;
} );
EOF

Nota: Il filtro xmlrpc_enabled disattiva solo i metodi che richiedono un login, come dice la documentazione di WordPress; il secondo filtro rimuove i metodi pingback. PHP gira comunque a ogni richiesta, quindi sotto un attacco pesante il blocco sul web server è più leggero.

I plugin fanno lo stesso: plugin dedicati "Disable XML-RPC", e plugin di sicurezza come Wordfence e Solid Security hanno un'opzione apposita (a novembre 2026). Un plugin si disattiva facilmente per errore durante un aggiornamento; il plugin must-use e la regola del web server no.

Far funzionare ancora Jetpack

Jetpack comunica con il tuo sito attraverso xmlrpc.php, quindi bloccarlo per tutti interrompe la connessione di Jetpack. Jetpack pubblica gli intervalli di indirizzi dei suoi server nella sua pagina di allowlist: consenti quelli e blocca tutti gli altri. In nginx la location deve poi passare anche la richiesta a PHP:

nginx
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;
}

Copia le righe PHP dal blocco location ~ \.php$ del sito: il percorso del socket cambia da sistema a sistema. In Apache, Require ip da solo consente solo quegli intervalli:

Apache
<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: Intervalli come pubblicati da Jetpack a novembre 2026. Possono cambiare: ricontrolla la pagina ogni tanto. Dietro Cloudflare o un altro proxy, queste regole vedono l'indirizzo del proxy, a meno di ripristinare prima l'indirizzo reale del visitatore (real_ip di nginx, mod_remoteip di Apache).

Limitare xmlrpc.php quando deve restare aperto

Se un'app o un servizio che non puoi elencare per indirizzo ha bisogno di XML-RPC, limita quanto spesso ogni indirizzo può chiamarlo. In nginx, definisci una zona nel contesto http, poi usala per xmlrpc.php: qui 6 richieste al minuto per indirizzo, con un burst di 5, e 429 oltre quella soglia.

nginx
# /etc/nginx/conf.d/xmlrpc-limit.conf (read inside the http { } block)
limit_req_zone $binary_remote_addr zone=xmlrpc:10m rate=6r/m;
nginx
# 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;
}
Qualsiasi distribuzione
sudo nginx -t && sudo systemctl reload nginx

Nota: Un limite conta le richieste, non le password: una sola richiesta system.multicall entro il limite può contenere ancora centinaia di tentativi. Abbinalo a password robuste, o banna gli indirizzi con fail2ban. Apache non ha un equivalente integrato; usa fail2ban qui sotto.

Bannare gli attaccanti XML-RPC con fail2ban

fail2ban legge il log di accesso e banna un indirizzo sul firewall, quindi funziona sia con nginx sia con Apache. Installalo come in Come bloccare gli attacchi brute force SSH (repository di Debian e Ubuntu, EPEL sulla famiglia RHEL). Poi crea un filtro per le richieste POST a xmlrpc.php:

Qualsiasi distribuzione
sudo tee /etc/fail2ban/filter.d/wordpress-xmlrpc.conf > /dev/null <<'EOF'
[Definition]
failregex = ^<HOST> \S+ \S+ \[[^]]+\] "POST /xmlrpc\.php
ignoreregex =
EOF

E una jail: 5 richieste in 60 secondi, un ban di un'ora. Metti il tuo indirizzo in ignoreip e, se usi Jetpack, i suoi intervalli. Per Apache, imposta logpath su /var/log/apache2/*access.log su Debian e Ubuntu o su /var/log/httpd/*access_log sulla famiglia RHEL.

Qualsiasi distribuzione
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
EOF

Prova il filtro sul log reale prima di attivarlo: fail2ban-regex mostra quante righe corrispondono. Poi riavvia fail2ban e controlla la jail.

Qualsiasi distribuzione
sudo fail2ban-regex /var/log/nginx/access.log /etc/fail2ban/filter.d/wordpress-xmlrpc.conf
sudo systemctl restart fail2ban
sudo fail2ban-client status wordpress-xmlrpc

Attenzione: Dietro Cloudflare o un altro proxy, il log di accesso mostra l'indirizzo del proxy: fail2ban bannerebbe il proxy e metterebbe il sito offline per molti visitatori. Ripristina prima l'indirizzo reale nel log, o blocca sul proxy.

Come InfraFence gestisce gli attacchi XML-RPC

Il web firewall di InfraFence legge i log di accesso di nginx, Apache e LiteSpeed, e dall'agent 1.0.42 anche quelli di Traefik, Caddy, HAProxy e dei web server nei container. Dietro Cloudflare prende l'indirizzo del visitatore da CF-Connecting-IP, e solo dagli intervalli di Cloudflare.

  • Ogni 5 richieste POST a xmlrpc.php da un indirizzo su un sito entro un minuto aggiungono 25 punti al punteggio di quell'indirizzo. Un attacco prolungato continua ad aggiungerli.
  • Il punteggio raccoglie anche gli altri attacchi dell'indirizzo, come il brute force su wp-login.php, la scansione dei plugin o i tentativi di exploit, e cala di 5 punti al minuto, così una singola richiesta strana svanisce.
  • A 80 punti l'indirizzo viene bannato sul firewall e l'evento è segnalato come critico. Soglie e punti si possono cambiare per server, e la modalità monitoraggio segnala senza bannare.

Vedi Web firewall per le regole e il punteggio. Cosa non fa: conta le richieste, quindi non può vedere quante password contiene una richiesta system.multicall; non modifica la configurazione di nginx, Apache o WordPress, quindi xmlrpc.php resta aperto finché non lo blocchi come sopra; e non ha un'eccezione integrata per Jetpack: se i server di Jetpack chiamano abbastanza spesso da prendere punti, aggiungi i loro intervalli alla whitelist. Il ban arriva dal log, quindi la richiesta che lo ha fatto scattare ha già avuto risposta.

Domande

È sicuro disattivare xmlrpc.php?
Per la maggior parte dei siti sì: l'editor a blocchi e l'area di amministrazione usano la REST API, non XML-RPC. Controlla prima se usi Jetpack, i pingback, un'app o uno strumento di pubblicazione che dipende ancora da XML-RPC.
Perché gli attacchi hanno stato 200?
XML-RPC risponde 200 e riporta il login fallito dentro la risposta. Il codice di stato non dice niente sul fatto che una password abbia funzionato.
Il mio plugin che limita i login non lo ferma. Perché?
Molti limitatori contano i tentativi su wp-login.php. Una richiesta system.multicall attraverso xmlrpc.php porta centinaia di tentativi in una sola richiesta e li aggira. Blocca o limita xmlrpc.php stesso.

Provalo gratis su 1 server

Starter è gratuito per un server, con protezione completa e blocco automatico. Senza carta di credito.

Installa l'agent come root, con il tuo token al posto di <TOKEN>:

curl -fsSL https://github.com/infrafence/infrafence-agent/releases/latest/download/install.sh | sudo bash -s -- --token <TOKEN>

Il token si crea nella dashboard, in Server › Aggiungi server.