XML-RPC permet d’échanger des données entre un site WordPress et une application externe sans passer par un navigateur web (Firefox, Chrome, Edge…). Par exemple, il est possible de récupérer un article ou au contraire d’en publier à partir d’une application installée sur un smartphone.
Le souci, c’est que derrière l’application, il n’y a pas toujours quelqu’un de bien intentionné. Voilà pourquoi il est conseillé de bloquer l’accès XML-RPC… sauf pour les extensions actives sur votre site et qui l’utilisent.
En savoir un peu plus sur XML-RPC
XML-RPC échange les données en utilisant le protocole internet HTTP.
HTTP(S) permet notamment d’accéder à un site en indiquant son adresse web dans la ligne de commande d’un navigateur web, par exemple : « https://dfarnier.fr/xmlrpc.php?test=oui ».
XML-RPC utilise la méthode ‘POST’ de HTTP en incluant des données au format XML dans le corps de la requête. Par exemple, la demande :
<?xml version="1.0" encoding="UTF-8"?> <methodCall><methodName>wp.getUsersBlogs </methodName> <params><param><value> AdminWP1 </value></param> <param><value> mdp01 </value></param></params> </methodCall>
peut permettre d’obtenir en retour les informations suivantes :
<?xml version="1.0" encoding="UTF-8"?> <methodResponse> <params> <param> <value> <array><data> <value><struct> <member><name>isAdmin</name><value><boolean>1</boolean></value></member> <member><name>url</name><value><string>http://localhost/wordpress/</string></value></member> <member><name>blogid</name><value><string>1</string></value></member> <member><name>blogName</name><value><string>WampServer</string></value></member> <member><name>xmlrpc</name><value><string>http://localhost/wordpress/xmlrpc.php</string></value></member> </struct></value> </data></array> </value> </param> </params> </methodResponse>
Dans l’exemple ci-dessus :
- la requête demande les informations relatives à un utilisateur (wp.getUsersBlogs) d’identifiant AdminWP1 et de mot de passe mdp01,
- le site WordPress retourne alors des informations sur cet utilisateur.
On trouvera la liste des fonctions XML-RPC disponibles pour WordPress dans le codex WordPress. Ces fonctions concernent les articles, les pages, les taxonomies, les médias, les commentaires, les options et les utilisateurs.
Quel est le problème de sécurité ?
XML-RPC permet d’insérer de multiples demandes dans une même requête. Dans l’exemple ci-dessous, un pirate recherche le mot de passe d’un utilisateur « canadawebservices » en tentant de nombreuses possibilités ( « 00 », « 000 », …, « 007007 », « 010101 »…) :
<value><struct><member><name>methodName</name><value><string>wp.getUsersBlogs</string></value></member><member><name>params</name><value><array><data><value><array><data><value><string>canadawebservices</string></value><value><string>00</string></value></data></array></value></data></array></value></member></struct></value> <value><struct><member><name>methodName</name><value><string>wp.getUsersBlogs</string></value></member><member><name>params</name><value><array><data><value><array><data><value><string>canadawebservices</string></value><value><string>000</string></value></data></array></value></data></array></value></member></struct></value> <value><struct><member><name>methodName</name><value><string>wp.getUsersBlogs</string></value></member><member><name>params</name><value><array><data><value><array><data><value><string>canadawebservices</string></value><value><string>0000</string></value></data></array></value></data></array></value></member></struct></value> <value><struct><member><name>methodName</name><value><string>wp.getUsersBlogs</string></value></member><member><name>params</name><value><array><data><value><array><data><value><string>canadawebservices</string></value><value><string>000000</string></value></data></array></value></data></array></value></member></struct></value> <value><struct><member><name>methodName</name><value><string>wp.getUsersBlogs</string></value></member><member><name>params</name><value><array><data><value><array><data><value><string>canadawebservices</string></value><value><string>00000000</string></value></data></array></value></data></array></value></member></struct></value> <value><struct><member><name>methodName</name><value><string>wp.getUsersBlogs</string></value></member><member><name>params</name><value><array><data><value><array><data><value><string>canadawebservices</string></value><value><string>007007</string></value></data></array></value></data></array></value></member></struct></value> <value><struct><member><name>methodName</name><value><string>wp.getUsersBlogs</string></value></member><member><name>params</name><value><array><data><value><array><data><value><string>canadawebservices</string></value><value><string>010101</string></value></data></array></value></data></array></value></member></struct></value>
La méthode, appelée « par force brute », peut finir par payer. Les requêtes pouvant être créées par programme, les pirates n’hésitent pas à tester des quantités énormes de combinaisons « identifiant / mot de passe »..
Comment bloquer XML-RPC ?
Le code suivant doit être ajouté dans une extension (plugin) ou dans le fichier « functions.php » du thème actif :
add_filter ('xmlrpc_enabled', '__return_false');
L’instruction consiste à retourner la valeur « false » en retour du crochet « xmlrpc_enabled« .
Pour être précis, le crochet « xmlrpc_enabled » ne bloque pas toutes les fonctions XML-RPC mais seulement celles qui nécessitent de s’identifier (s’authentifier). C’est suffisant pour bloquer les attaques par force brute car ce crochet bloque notamment la fonction « wp.getUsersBlogs ».
Voici la réponse désormais retournée par le serveur à une demande de type « wp.getUsersBlogs » :
<?xml version="1.0" encoding="UTF-8"?> <methodResponse> <fault> <value><struct><member> <name>faultCode</name><value><int>405</int></value></member> <member> <name>faultString</name> <value><string>Les services XML-RPC sont désactivés sur ce site.</string></value></member></struct></value> </fault> </methodResponse>Haut de page
Ne pas bloquer Jetpack
Jetpack est une extension populaire qui offre de nombreuses fonctionnalités permettant d’améliorer un site WordPress. Or, si on bloque XML-RPC, Jetpack ne fonctionne plus, car il l’utilise pour assurer le dialogue entre le site et les serveurs de la société Automattic.
En fait, Jetpack ne passe pas par le filtre « xmlrpc_enabled ». Ce filtre n’est utilisé que pour l’authentification des appels à XML-RPC, or Jetpack utilise un processus d’authentification différent du processus proposé par WordPress en standard.
Néanmoins, j’ai contacté le support Jetpack pour leur demander si cela sera toujours vrai pour tous les module Jetpack à l’avenir. En gros, la réponse est que c’est fortement probable, mais que cela ne peut être garanti (« we don’t actively test against that type of setup so if you notice anything odd, please do let us know« ). Nous allons donc mettre en œuvre une solution permettant de se garantir de ce risque très faible. Franchement, c’est limite parano, mon objectif est plutôt de montrer comment faire pour d’autres extensions.
Toute requête Jetpack comprend le paramétrage « ?for=Jetpack » dans l’adresse web. Exemple de requête Jetpack :
- dans l’adresse web (url) :
https://dfarnier.fr/xmlrpc.php?for=jetpack&token=t8sV%2961IS%2663QO%5Eh7J%218dl%28Zdx1x8O47%3A1%3A0×tamp=1501864171&nonce=scIiWQolC8&body-hash=PBiAPcy9QkOh1c7U7G5hw%2FJuT%2FI%3D&signature=7vCVIXsZLU0IFAG1l8%2F%2F88Co%2F7U%3D
- dans le corps de la requête
<?xml version="1.0"?> <methodCall><methodName>system.multicall</methodName> <params><param><value><array><data> <value><struct> <member><name>methodName</name><value><string>jetpack.jsonAPI</string></value></member> <member><name>params</name><value><array><data> <value><array><data> <value><string>GET</string></value> <value><string>https://public-api.wordpress.com/rest/v1.1/sites/98108117?http_envelope=1</string></value> <value><string></string></value> <value><int>98108117</int></value> <value><array><data></data></array></value> <value><string></string></value> </data></array></value> <value><boolean>0</boolean></value> </data></array></value></member> </struct></value> </data></array></value></param> </params></methodCall>
Nous ajoutons donc un test afin de ne pas bloquer XML-RPC lorsque la requête provient de Jetpack :
« if ( isset( $_GET[‘for’] ) && ‘jetpack’ == $_GET[‘for’] ) » vérifie si le paramétrage contenu dans l’adresse web contient « for=jetpack ». Si oui, les requêtes XML-RPC sont autorisées, sinon XML-RPC est bloqué.
Toutes les requêtes contenant « for=jetpack » dans le paramétrage de l’adresse web sont prises en charge par l’extension Jetpack si elle est activée. Jetpack est capable de s’assurer que la requête provient effectivement d’un serveur de la société Automattic. Ainsi, on est protégé contre un pirate qui se contenterait d’ajouter « for=jetpack » dans ses requêtes.
À noter : cette façon de procéder est préférable à celles consistant à vérifier si la requête provient d’un serveur de la société Automattic (vérification à partir de l’adresse IP). En effet, le support Jetpack met en garde sur le fait que les adresses IP peuvent changer.
Et si Jetpack n’est pas activé ? La réponse est simple : nous avons introduit une faille de sécurité car dans ce cas XML-RPC va traiter les requêtes pour laquelle un paramétrage « for=Jetpack » est introduit dans l’adresse web.
Nous devons donc compléter le code pour qu’il s’assure que l’extension Jetpack est active :
function wpdf_bloque_XMLRPC () { if ( isset( $_GET['for'] ) && 'jetpack' == $_GET['for'] ) { include_once( ABSPATH . 'wp-admin/includes/plugin.php' ); return is_plugin_active('jetpack/jetpack.php'); } return false; } // fin fonction wpdf_bloque_XMLRP
Le marqueur conditionnel is_plugin_active() appliqué à l’extension Jetpack renvoie « true » si l’extension est active et « false » sinon.
À noter : pour pouvoir utiliser le marqueur conditionnel ‘is_active_plugin() », le fichier « plugin.php » contenu dans le répertoire « wp-admin/includes » doit être préalablement chargé, ce qui est réalisé par l’instruction « include_once( ABSPATH . ‘wp-admin/includes/plugin.php’ ); »
Et pour d’autres applications?
D’autres extensions utilisent XML-RPC. Pour bloquer XML-RPC sans bloquer le fonctionnement normal de ces extensions, il faut pratiquer comme pour Jetpack :
- disposer d’un moyen permettant d’identifier la source de la requête (interroger le support de l’application ou de l’extension),
- modifier le code proposé pour Jetpack afin de ne pas bloquer XML-RPC s’il s’agit d’un appel provenant d’une extension valide
- vérifier que l’xtension appelante est activée.
Le code de l’extension
Voici le code à insérer pour Jetpack :
- dans le fichier » functions.php » (facile à installer, mais qui disparaît si on met à jour ou on change le thème WordPress courant)
ou
- dans un fichier d’extension déposé dans le répertoire « wp-content/plugins » (ce qui permet d’activer ou désactiver l’extension) ou dans le répertoire « wp-content/mu-plugins » (l’extension est alors toujours active) :
Si vous n’en êtes pas (encore) familier, l’article ci-dessous décrit l’utilisation du fichier de thème « functions.php », des extensions (plugins) et des extensions automatiques (mu-plugins) :
Hello,
c’est une bonne pratique de sécurité que de vouloir bloquer XML-RPC si nous n’en avons pas l’utilité. Par contre, un blocage au niveau serveur (si on y a accès) reste la méthode la plus efficace car moins gourmande en termes de ressources. On peut passer par fail2ban pour faire cela.
Bonjour Aurélien,
Merci pour ce complément d’information.
En effet, la solution fail2ban ne doit pas être utilisée si on utilise par exemple Jetpack et probablement pas réalisable si on est sur un mutualisé.
Je dirais que la solution que je propose est plutôt adaptée à des amateurs / débutants (ce qui est le propos de mon site) ta solution étant plutôt professionnelle.
Daniel, qui a débuté WordPress en regardant WP Channel
Mais je t’en prie ! 🙂
Dans fail2ban j’exclue les serveurs d’Automattic. 😉
Bonne remarque, mais attention : les serveurs d’Automattic peuvent évoluer, c’est pourquoi le support Jetpack déconseille de se baser sur les adresses IP (j’avais trouvé une réponse de Jérémy Hervé sur ce point).