Quand une requête SQL est construite en concaténant directement une valeur saisie par l’utilisateur, cette valeur peut contenir des fragments de syntaxe SQL qui altèrent la requête d’origine, un peu comme glisser une phrase supplémentaire dans un formulaire administratif pour qu’elle soit lue comme une instruction officielle plutôt qu’une simple donnée.
Protection dans WordPress
La classe $wpdb fournit la méthode prepare(), qui sépare strictement la structure de la requête des valeurs qui y sont injectées, ces dernières étant automatiquement échappées selon leur type (%s pour une chaîne, %d pour un entier). C’est la méthode recommandée dès qu’une requête personnalisée intègre une donnée externe.
Exemple
global $wpdb;
// vulnérable : concaténation directe
$wpdb->query("SELECT * FROM {$wpdb->posts} WHERE post_title = '$titre'");
// sécurisé : requête préparée
$sql = $wpdb->prepare(
"SELECT * FROM {$wpdb->posts} WHERE post_title = %s",
$titre
);
$wpdb->get_results($sql);
Pièges fréquents
Beaucoup pensent à protéger le champ texte évident d’un formulaire, mais oublient un paramètre d’URL utilisé tel quel dans une clause ORDER BY ou LIMIT : ces emplacements échappent parfois au réflexe d’échappement car ils ne semblent pas provenir « directement » d’une saisie utilisateur. Toute valeur qui entre dans une requête SQL sans passer par prepare() doit être considérée comme un point d’entrée potentiel, quelle que soit son origine apparente, y compris un identifiant de colonne dynamique qui ne peut pas être substitué par un simple marqueur %s et doit être validé contre une liste blanche de noms autorisés.