WordPress XML-RPC: 3 manieren om het uit te schakelen & xmlrpc.php-aanvallen herkennen | Jetpack-uitzondering

Zie je in de toegangslog van je WordPress-website honderden xmlrpc.php-verzoeken achter elkaar vanaf hetzelfde IP, dan is dat wachtwoordraden gericht op WordPress XML-RPC. Ik heb 8.624 regels uit één dag toegangslog van een Nginx + Apache-server met meerdere WordPress-sites ingedeeld, de drie manieren om het uit te schakelen zelf toegepast en de responscodes gecontroleerd, en zet hier op een rij op welke laag je moet blokkeren om ook de PHP-uitvoering terug te dringen.
XML-RPC is een protocol dat XML-documenten via HTTP uitwisselt om WordPress-functies op afstand aan te roepen. Sinds WordPress 3.5 staat er in de instellingen geen schakelaar meer voor, dus het staat vanaf de installatie aan.
1️⃣ Wat xmlrpc.php doet en wie het gebruikt
Sinds de REST API er is, lijkt [xmlrpc.php] een bestand dat niemand meer gebruikt, maar Jetpack verbindt je site nog altijd via dit bestand met WordPress.com. De officiële Jetpack-documentatie zegt uitdrukkelijk dat het van de XML-RPC-verbinding afhangt, en bij hosting die XML-RPC standaard blokkeert, mislukt de Jetpack-verbinding helemaal. Het is als een kantoor dat nog steeds één faxnummer aanhoudt.
Vanaf WordPress 7.1 accepteert dit bestand 77 methoden: 50 uit de wp-familie voor berichten, media en reacties, 23 uit de oude blogprotocollen (MetaWeblog, MovableType, Blogger), 2 voor pingbacks en 2 demo-methoden om te testen.
De wp-familie logt in met de gebruikersnaam en het wachtwoord in de body van het verzoek en gaat dus niet via het inlogscherm. Berichten schrijven en bewerken, media uploaden en reacties beheren vallen allemaal onder deze familie; de 23 oude blogprotocolmethoden zijn gebleven zodat berichten op dezelfde manier als met andere blogtools kunnen worden uitgewisseld.
demo.sayHello is een testaanroep die in WordPress XML-RPC is ingebouwd. Stuur je hem, dan krijg je alleen het woord [Hello!] terug; omdat er geen login voor nodig is, wordt hij gebruikt om te controleren of XML-RPC werkt. Zijn tegenhanger demo.addTwoNumbers telt twee getallen op en geeft de som terug.
Ze reageren allemaal alleen op een XML-body die via POST wordt verstuurd; open je het adres in een browser, dan krijg je een 405 met de melding [XML-RPC server accepts POST requests only.].
Ook pingbacks lopen via dit bestand. Publiceer je een bericht, dan gaat er een pingback naar de xmlrpc.php van elke site waarnaar je in de tekst linkt, en de ontvangende server opent zelf het verzendende adres om te controleren of de link echt bestaat.
2️⃣ xmlrpc.php-aanvallen in de toegangslog
Wachtwoordraden gaat niet via het inlogscherm, maar komt binnen via [xmlrpc.php]. In de toegangslog van een Nginx + Apache-server met meerdere WordPress-sites stonden op 14 oktober van 0:00 tot 20:00 uur 8.624 POST /xmlrpc.php-verzoeken van 72 IP-adressen, en 19 daarvan stuurden er precies 300.
Eén IP stuurde in 28 minuten, van 14:09 tot 14:37 uur, 300 verzoeken met tot het eind een vaste tussenpoos van 5 à 6 seconden. 8.524 verzoeken kwamen binnen met Jetpack of WordPress.com in de User-Agent, en hetzelfde IP wisselde meerdere waarden af die het echte Jetpack-formaat nabootsten, zoals [Jetpack by WordPress.com (Jetpack 13.0; WordPress 6.3)].
Tot 14:00 uur die dag stuurde de XML-RPC-uitschakeling van de beveiligingsplugin deze verzoeken met een 302 terug naar de homepage; vanaf 14:00 uur, na de blokkade in Nginx, eindigden ze met een 403. Een 302 betekent dat PHP voor elk verzoek draaide, ook al was de body 0 bytes; een 403 is een antwoord dat PHP nooit heeft geraakt.
3️⃣ system.multicall en wachtwoordraden
Veel uitleg zegt dat je met [system.multicall] in één verzoek honderden wachtwoorden kunt testen, maar zodra een login binnen een verzoek één keer mislukt, controleert WordPress de wachtwoorden van de overige aanroepen niet meer. Alleen de eerste aanroep gaat door wp_authenticate; de rest krijgt zonder controle dezelfde fout terug: [Incorrect username or password.].
Omdat er voor elke aanroep toch een antwoord terugkomt, levert één verzoek met 100 verkeerde wachtwoorden binnen 0,12 seconden 100 fouten op in een antwoord van 23 KB. Staat het juiste wachtwoord verderop in hetzelfde verzoek, dan wordt het samen met de eerdere mislukking geweigerd; staat het vooraan, dan slaagt alleen die aanroep. Het wachtwoord wordt maar één keer vergeleken, maar het verwerken van de XML en het laden van WordPress gebeuren bij elk verzoek opnieuw, dus de serverbelasting van deze aanval komt niet van de logincontrole maar van het aantal verzoeken zelf.
4️⃣ 3 manieren om XML-RPC uit te schakelen en hun responscodes
Ook als je XML-RPC in een plugin uitschakelt, komen verzoeken aan [xmlrpc.php] tot in PHP. Met het filter actief stuurde ik een verzoek met 100 aanroepen en kreeg ik gewoon HTTP 200 terug met 100 fouten (24 KB). Afhankelijk van de laag die het verzoek weigert, zijn er drie methoden, elk met een andere responscode.
1. Uitschakelen in code met het filter xmlrpc_enabled
Zet in de functions.php van je thema één add_filter-regel die het filter [xmlrpc_enabled] false laat teruggeven. Aanroepen waarvoor een login nodig is, worden dan geweigerd met de fout [XML-RPC services are disabled on this site.] en HTTP 405, maar de testaanroep demo.sayHello, waarvoor geen login nodig is, antwoordt nog steeds met 200.
2. Uitschakelen in de plugininstellingen: Perfmatters · Hide My WP
Hierbij schakel je het uit met een Disable XML-RPC-instelling in een plugin zoals Perfmatters of Hide My WP. Hide My WP gebruikt hetzelfde filter als hierboven en geeft dus dezelfde antwoorden, maar Perfmatters beëindigt bovenop dat filter directe verzoeken aan [xmlrpc.php] met een 403 en verwijdert ook de pingback-header en de pingback-link; na het uitschakelen antwoorden GET en POST dus allebei met 403. Om die 403 te geven, draait WordPress echter nog tot aan de init-fase, waardoor het antwoord 0,13–0,15 seconden duurt, net zo lang als wanneer het openstaat, en de PHP-belasting blijft.
3. Blokkeren op de webserver: Nginx · Apache
Of het nu Nginx of Apache is, het verzoek wordt vóór PHP geweigerd, dus anders dan bij de pluginmethoden draait WordPress helemaal niet.
# Nginx — in het server-blok, boven de PHP-location
location = /xmlrpc.php {
deny all;
}Zet dit location-blok boven de PHP-location; Nginx weigert dan meteen elk verzoek dat precies overeenkomt met [xmlrpc.php]. Test na het toevoegen de configuratie en laad die opnieuw, anders wordt het niet toegepast.
nginx -tnginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successfulsystemctl reload nginxVanaf het herladen eindigen GET en POST met een 403 zonder PHP te bereiken; de body is de standaard 403-pagina van Nginx van 153 bytes en het antwoord duurt 0,02 seconden.
# Apache — .htaccess in de hoofdmap van de site (Apache 2.4)
<Files xmlrpc.php>
Require all denied
</Files>Op een server met alleen Apache zet je de Files-instructie hierboven in de .htaccess in de hoofdmap van de site; die geldt meteen, en de standaard 403-pagina van Apache (199 bytes) komt in dezelfde 0,02 seconden terug.
curl -s -o /dev/null -w '%{http_code}' -X POST https://example.com/xmlrpc.php -d '<methodCall><methodName>demo.sayHello</methodName></methodCall>'403Het commando hierboven stuurt de testaanroep demo.sayHello via POST en controleert de responscode; na een blokkade op de webserver komt er 403 terug. Laat je de opties [-o /dev/null] en [-w] weg, dan zie je de body: komt er [Hello!] terug, dan staat het open. Een site die alleen met het filter is uitgeschakeld geeft hetzelfde antwoord, dus om de 405 te zien moet je ook een login-aanroep sturen.
5️⃣ Wanneer je het niet moet uitschakelen, en de alternatieven
Alles in één keer op de webserver blokkeren heeft het grootste effect, maar past niet bij sites die via Jetpack met WordPress.com zijn verbonden. Jetpack hangt af van de XML-RPC-verbinding, en bij hosting die XML-RPC standaard blokkeert, zoals WP Engine, moet je volgens de instructies van Jetpack een uitzondering openzetten voordat de verbinding werkt. Hetzelfde geldt voor sites die pingbacks willen ontvangen.
Zulke sites beperken het door alleen de IP-lijst toe te laten die Jetpack publiceert (jetpack.com/ips-v4.txt) en de rest te weigeren. Zet in het location-blok van Nginx een allow-regel per IP-bereik en eindig met deny all.
Kun je het niet volledig blokkeren, dan blijft er een limiet per minuut over voor verzoeken die niet zijn ingelogd. De instelling is dezelfde als het onderdeel over de Nginx-snelheidsbeperking in het artikel wp-admin beveiligen — omdat alleen verzoeken zonder login-cookie worden geteld, heeft het geen invloed op beheerwerk terwijl je bent ingelogd.
🔢 FAQ & aanbevolen content










ℹ️ Affiliatemelding
De content van deze site bevat affiliatelinks. Koopt een bezoeker via een van deze links een product of dienst, dan ontvangt deze site een commissie van de verkoper. Het bedrag dat de koper betaalt (bij acties gaat het omlaag ↓) wordt daardoor niet hoger. De vermelde prijzen, kortingen en voorraad gelden op het moment van schrijven en kunnen afwijken; controleer ze daarom vóór je aankoop bij de verkoper. Producten worden gekozen en beoordeeld volgens onze eigen criteria, en commissies hebben geen invloed op de volgorde of de inhoud van de beoordelingen.