Injection SQL sur WordPress : comprendre le risque réel
Une injection SQL apparaît lorsqu’une donnée contrôlée par l’utilisateur est interprétée comme une partie de requête SQL. Sur WordPress, le cœur limite fortement ce risque lorsqu’on utilise les API prévues. Les problèmes arrivent surtout dans les plugins, thèmes, snippets ou développements sur mesure qui construisent des requêtes à la main.
Pourquoi WordPress n’immunise pas un code personnalisé
WordPress fournit $wpdb, des fonctions de validation, des APIs de contenus et de métadonnées. Mais si un développeur concatène directement $_GET, $_POST ou une valeur de cookie dans une requête, la protection disparaît. Une seule recherche, un filtre, un tri ou un paramètre d’URL peut devenir exploitable.
Les attaques modernes ne cherchent pas toujours à afficher une erreur SQL. Elles peuvent être aveugles, lentes, limitées à une extraction progressive, ou combinées à une faille d’upload pour prendre le contrôle complet du site.
La défense principale : les requêtes préparées
Avec $wpdb, utilisez $wpdb->prepare() pour séparer le code SQL des données. Les placeholders doivent correspondre au type attendu : chaîne, entier, flottant. La validation reste nécessaire avant la requête, mais elle ne remplace pas la préparation.
$search = sanitize_text_field( wp_unslash( $_GET['q'] ?? '' ) );
$sql = $wpdb->prepare(
"SELECT ID FROM {$wpdb->posts} WHERE post_title LIKE %s AND post_status = %s",
'%' . $wpdb->esc_like( $search ) . '%',
'publish'
);
$ids = $wpdb->get_col( $sql );
Pour les clauses dynamiques, ne préparez pas uniquement les valeurs. Contrôlez aussi les noms de colonnes, l’ordre de tri et les opérateurs avec des listes blanches. Un ORDER BY ne doit jamais reprendre directement une valeur d’URL.
Validation, échappement et permissions
- Valider les types : entier pour un ID, email pour une adresse, slug pour une URL courte.
- Utiliser
sanitize_text_field(),absint(),sanitize_key()selon le contexte. - Échapper la sortie HTML avec
esc_html(),esc_attr()ouesc_url(). - Vérifier les capacités avec
current_user_can()avant toute action sensible. - Limiter les messages d’erreur SQL en production.
Audit après piratage
Après une compromission, il faut chercher plus loin qu’une simple requête vulnérable. Contrôlez les comptes administrateurs, les options WordPress, les tâches cron, les fichiers récemment modifiés, les tables ajoutées et les contenus injectés dans les articles. Une injection SQL peut servir à créer un utilisateur, déposer du spam SEO, modifier des redirections ou préparer une attaque persistante.
Checklist développeur
- Aucune concaténation directe de données utilisateur dans SQL.
$wpdb->prepare()sur toutes les requêtes personnalisées.- Listes blanches pour colonnes, tris et filtres.
- Logs d’erreurs non exposés au public.
- Comptes MySQL avec droits limités.
- Tests avec caractères spéciaux, apostrophes, encodages et paramètres absents.
La prévention de l’injection SQL repose sur une discipline simple : aucune donnée externe n’est fiable tant qu’elle n’a pas été validée, typée, préparée et utilisée dans le bon contexte.
