Fermer

janvier 20, 2026

Comment prévenir les attaques par épuisement des ressources sur la recherche WordPress (MySQL DoS)

Comment prévenir les attaques par épuisement des ressources sur la recherche WordPress (MySQL DoS)


J’examinais mes analyses hier soir et j’ai vu des milliers de visiteurs, le délai d’administration de l’administrateur et celui de mon serveur Processeur était en pointe. Après avoir déjà combattu et déjoué les attaques dernier trimestrej’ai eu une longueur d’avance sur le dépannage et j’ai trouvé le coupable : Recherche interne de WordPress.

Remarque complémentaire : La croissance de Martech Zone semble vraiment agacer quelqu’un ! Je ne suis pas sûr de ce que j’ai fait pour mériter ce genre d’attention, mais ouais…

Les attaquants et les robots agressifs exploitent souvent la valeur par défaut WordPress mécanisme de recherche pour lancer un Attaque d’épuisement des ressources. Il s’agit d’un type de déni de service au niveau de la couche application (De la) attaque conçue non pas pour voler des données, mais pour submerger votre serveur de base de données jusqu’à ce qu’il abandonne.

Cet article explique pourquoi cela se produit et comment confirmer que vous êtes attaqué en utilisant MySQL outils et comment implémenter une défense de code à deux couches pour l’arrêter.

L’anatomie de l’attaque

Pour comprendre la défense, vous devez comprendre la faiblesse.

Par défaut, WordPress effectue des recherches en utilisant MySQL LIKE requêtes avec des caractères génériques aux deux extrémités (par exemple, LIKE '%term%'). En termes de base de données, un caractère générique principal force MySQL à effectuer une Analyse complète du tableau. Il ne peut pas utiliser efficacement les index standards ; il doit littéralement lire chaque ligne de votre wp_posts table du disque pour vérifier si le terme existe quelque part dans le contenu.

L’effet multiplicateur

L’attaque fonctionne en envoyant des requêtes de recherche massives contenant des dizaines ou des centaines de mots.

Si un robot recherche 50 mots aléatoires, WordPress les traduit en une seule requête SQL contenant 50 mots distincts. AND ... LIKE '%word%' clauses. MySQL doit désormais effectuer des références croisées complexes sur des milliers de lignes pour 50 termes différents simultanément.

Quelques dizaines de ces requêtes par seconde peuvent facilement paralyser un serveur MySQL standard.

Diagnostic : comment savoir que vous êtes attaqué

Si votre site est bloqué, vous devez examiner ce que fait actuellement votre base de données.

Méthode 1 : AFFICHER LA LISTE DES PROCESSUS (diagnostic immédiat)

Si vous disposez d’un accès SSH à votre serveur ou d’un accès à phpMyAdmin, exécutez la commande SQL suivante :

SHOW FULL PROCESSLIST;

Regardez les résultats. Si vous faites l’objet d’une attaque de recherche, vous verrez de nombreux processus dans un état « Envoi de données » ou « Verrouillé » exécutant des requêtes très longues sur wp_posts ça ressemble à ça :

SELECT SQL_CALC_FOUND_ROWS wp_posts.ID FROM wp_posts WHERE 1=1 AND (
(wp_posts.post_title LIKE '%word1%') OR (wp_posts.post_content LIKE '%word1%')
) AND (
(wp_posts.post_title LIKE '%word2%') OR (wp_posts.post_content LIKE '%word2%')
) AND ... [repeating for 50+ words] ...
ORDER BY wp_posts.post_date DESC LIMIT 0, 10

Si vous voyez des piles de ces requêtes prendre plusieurs secondes à s’exécuter, vous subissez une attaque d’épuisement des ressources.

Méthode 2 : le journal des requêtes lentes (diagnostic historique)

Si votre hébergeur active le journal des requêtes lentes MySQL, vérifiez-le. Vous trouverez des entrées montrant des requêtes similaires à celle ci-dessus prenant un temps inhabituel (par exemple, Query_time: 5.2345) à exécuter.

La solution : une défense à double couche

Nous ne pouvons pas compter sur des plugins standards pour résoudre ce problème, car l’attaque se produit avant le chargement de la plupart des plugins. Nous devons intercepter la requête de recherche le plus tôt possible lors de l’exécution du noyau de WordPress : le pre_get_posts crochet.

Nous mettrons en œuvre une stratégie de défense à deux niveaux :

  1. Le videur (limite de caractères stricte) : Si une requête est étrangement longue (par exemple, plus de 200 caractères), nous considérons qu’elle est malveillante et la bloquons immédiatement avec un 403 Interdit erreur. Cela permet d’économiser PHP et MySQL de faire le gros du travail.
  2. Le filtre (optimisation des mots clés) : Pour les recherches légitimes mais verbeuses, nous supprimons les « mots vides » courants (comme « le », « un », « est »), puis tronçons la requête restante à un nombre sûr de mots-clés de grande valeur (par exemple, les 4 premiers).

Mise en œuvre

Ajoutez le code suivant à celui de votre thème actif functions.php fichier, ou de préférence, dans un plugin spécifique au site.

<?php
/**
 * Prevents Resource Exhaustion Attacks on WordPress Search.
 * * This hooks into pre_get_posts before SQL is generated to implement 
 * a double-layer defense:
 * Layer 1: A hard character limit that outright blocks massive queries with a 403.
 * Layer 2: A keyword optimizer that strips stop words and limits the term count to protect MySQL.
 */
add_action( 'pre_get_posts', 'protect_site_from_search_abuse' );

function protect_site_from_search_abuse( $query ) {
    if ( is_admin() || ! $query->is_main_query() || ! $query->is_search() ) {
        return;
    }
        
    $max_characters = 200; 
    $max_keywords   = 4;   

    $search_string = $query->get( 's' );

    if ( empty( $search_string ) ) {
        return;
    }
        
    // Layer 1: Character Limit
    if ( strlen( $search_string ) > $max_characters ) {
        wp_die( 'Search query too long.', 'Search Blocked', array( 'response' => 403 ) );
    }

    // Layer 2: Keyword Optimizer
    $stop_words = wp_get_search_stopwords();
    
    // UPDATED REGEX: Added \+ to handle plus signs in URLs
    $all_words = preg_split( '/[\s,\+]+/', $search_string, -1, PREG_SPLIT_NO_EMPTY );
    
    $meaningful_words = array_filter( $all_words, function( $word ) use ( $stop_words ) {
        return ! in_array( strtolower( $word ), $stop_words );
    });

    if ( count( $meaningful_words ) > $max_keywords ) {
        $truncated_array = array_slice( $meaningful_words, 0, $max_keywords );
        $query->set( 's', implode( ' ', $truncated_array ) );
    }
}
?>

Comment le tester

  1. Tester la couche 1 : Essayez de rechercher un bloc de texte massif (plus de 200 caractères). Vous devriez immédiatement voir une page d’erreur WordPress standard indiquant « Requête de recherche trop longue ».
  2. Tester la couche 2 : Recherchez une phrase longue et verbeuse comme : « Je recherche les meilleures recettes de pizza italienne du centre-ville de Chicago ».
  3. Vérifiez le titre de la page des résultats de recherche. Vous devriez voir que WordPress a en fait recherché quelque chose de beaucoup plus court, probablement uniquement les termes clés tels que : “recettes de pizza italienne chicago”.

En implémentant ce code, vous vous assurez que, quelles que soient les absurdités qu’un utilisateur ou un robot colle dans votre barre de recherche, votre base de données MySQL ne reçoit qu’une requête simple et gérable.




Source link