Nous identifions les « mauvais » bots potentiels et les bloquons par IP

Nous identifions les « mauvais » bots potentiels et les bloquons par IP

Bonjour ! Dans cet article, je vais expliquer comment les utilisateurs d'un hébergement classique peuvent identifier les adresses IP générant une charge excessive sur le site et les bloquer ensuite à l'aide des outils d'hébergement, il y aura un peu de code PHP, quelques captures d'écran.

Données d'entrée :

  1. Site créé sur CMS WordPress
  2. Hébergement Beget (ce n'est pas de la publicité, mais les captures d'écran du panneau d'administration seront effectivement de ce fournisseur d'hébergement)
  3. Le site WordPress a été lancé au début des années 2000 et contient de nombreux articles et matériaux.
  4. Version PHP 7.2
  5. WP a la dernière version.
  6. Depuis quelque temps, le site a commencé à générer une forte charge sur MySQL selon les données de l'hébergement. Chaque jour, cette valeur dépassait 120 % de la norme pour le compte.
  7. Selon Yandex.Metrica, le site reçoit entre 100 et 200 visiteurs par jour.

Dans un premier temps, il a été fait :

  1. Les tables de la base de données ont été nettoyées des déchets accumulés.
  2. Les plugins inutiles ont été désactivés, et les sections de code obsolètes ont été supprimées.

Je souligne que des options de mise en cache (plugins de mise en cache) ont été testées, des observations ont été réalisées - mais la charge de 120 % d'un seul site est restée constante et n'a pu qu'augmenter.

Voici à quoi ressemblait la charge approximative sur les bases de données d'hébergement.

Nous identifions les « mauvais » bots potentiels et les bloquons par IP
En tête se trouve le site dont il s'agit, juste en dessous d'autres sites ayant le même CMS et une fréquentation similaire, mais générant moins de charge.

Analyse

  • De nombreuses tentatives ont été faites avec des options de mise en cache des données, des observations ont été menées pendant plusieurs semaines (heureusement, l'hébergement ne m'a pas écrit une seule fois pendant ce temps pour me dire que j'étais si mauvais et que j'allais être déconnecté).
  • Une analyse a été effectuée pour trouver des requêtes lentes, puis la structure de la base de données et le type des tables ont été légèrement modifiés.
  • Pour l'analyse, AWStats intégré a été utilisé en premier lieu (il m'a d'ailleurs aidé à identifier la pire adresse IP en fonction du volume de trafic).
  • La métrique - la métrique ne fournit d'informations que sur les personnes, et non sur les bots.
  • Des tentatives ont été faites pour utiliser des plugins pour WP, capables de filtrer et de bloquer les visiteurs même en fonction de leur pays et de diverses combinaisons.
  • Une méthode radicale pour fermer le site pendant une journée avec la mention « Nous sommes en maintenance » a été réalisée à l'aide du célèbre plugin. Dans ce cas, la charge a diminué comme prévu, mais pas à zéro, car la philosophie de WP repose sur des hooks et les plugins commencent leur activité lorsque l'un de ces « hooks » se produit, et avant cela, des requêtes à la base de données peuvent déjà avoir été effectuées.

Idée

  1. Calculer les adresses IP qui effectuent de nombreuses requêtes sur une courte période.
  2. Enregistrer le nombre de visites sur le site.
  3. Sur la base du nombre de visites, bloquer l'accès au site.
  4. Bloquer en utilisant l'entrée « Deny from » dans le fichier .htaccess.
  5. D'autres options, comme iptables et les règles pour Nginx, ne seront pas considérées, car j'écris sur l'hébergement.

Une idée est apparue, il faut donc la réaliser, comment faire autrement...

  • Créons des tables pour stocker les données.
    CREATE TABLE `wp_visiters_bot` (
    	`id` INT(11) NOT NULL AUTO_INCREMENT,
    	`ip` VARCHAR(300) NULL DEFAULT NULL,
    	`browser` VARCHAR(500) NULL DEFAULT NULL,
    	`cnt` INT(11) NULL DEFAULT NULL,
    	`request` TEXT NULL,
    	`input` TEXT NULL,
    	`data_update` DATETIME NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    	PRIMARY KEY (`id`),
    	UNIQUE INDEX `ip` (`ip`)
    )
    COMMENT='Candidats à bloquer'
    COLLATE='utf8_general_ci'
    ENGINE=InnoDB
    AUTO_INCREMENT=1;
    

    CREATE TABLE `wp_visiters_bot_blocked` (
    	`id` INT(11) NOT NULL AUTO_INCREMENT,
    	`ip` VARCHAR(300) NOT NULL,
    	`data_update` DATETIME NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    	PRIMARY KEY (`id`),
    	UNIQUE INDEX `ip` (`ip`)
    )
    COMMENT='Liste des already bloqués'
    COLLATE='utf8_general_ci'
    ENGINE=InnoDB
    AUTO_INCREMENT=59;
    

    CREATE TABLE `wp_visiters_bot_history` (
    	`id` INT(11) NOT NULL AUTO_INCREMENT,
    	`ip` VARCHAR(300) NULL DEFAULT NULL,
    	`browser` VARCHAR(500) NULL DEFAULT NULL,
    	`cnt` INT(11) NULL DEFAULT NULL,
    	`data_update` DATETIME NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    	`data_add` DATETIME NULL DEFAULT CURRENT_TIMESTAMP,
    	PRIMARY KEY (`id`),
    	UNIQUE INDEX `ip` (`ip`)
    )
    COMMENT='Historique de toutes les requêtes pour débogage'
    COLLATE='utf8_general_ci'
    ENGINE=InnoDB
    AUTO_INCREMENT=1;
    
  • Créons un fichier dans lequel nous mettrons le code. Le code consistera à écrire dans les tables des candidats au blocage et à tenir un historique pour le débogage.

    Le code du fichier pour enregistrer les adresses IP.

    <?php
    
    if (!defined('ABSPATH')) {
        return;
    }
    
    global $wpdb;
    
    /**
     * Вернёт конкретный IP адрес посетителя
     * @return boolean
     */
    function coderun_get_user_ip() {
    
        $client_ip = '';
    
        $address_headers = array(
            'HTTP_CLIENT_IP',
            'HTTP_X_FORWARDED_FOR',
            'HTTP_X_FORWARDED',
            'HTTP_X_CLUSTER_CLIENT_IP',
            'HTTP_FORWARDED_FOR',
            'HTTP_FORWARDED',
            'REMOTE_ADDR',
        );
    
        foreach ($address_headers as $header) {
            if (array_key_exists($header, $_SERVER)) {
    
                $address_chain = explode(',', $_SERVER[$header]);
                $client_ip = trim($address_chain[0]);
    
                break;
            }
        }
    
        if (!$client_ip) {
            return '';
        }
    
    
        if ('0.0.0.0' === $client_ip || '::' === $client_ip || $client_ip == 'unknown') {
            return '';
        }
    
        return $client_ip;
    }
    
    $ip = esc_sql(coderun_get_user_ip()); // IP адрес посетителя
    
    if (empty($ip)) {// Нет IP, ну и идите лесом...
        header('Content-type: application/json;');
        die('Big big bolt....');
    }
    
    $browser = esc_sql($_SERVER['HTTP_USER_AGENT']); //Данные для анализа браузера
    
    $request = esc_sql(wp_json_encode($_REQUEST)); //Последний запрос который был к сайту
    
    $input = esc_sql(file_get_contents('php://input')); //Тело запроса, если было
    
    $cnt = 1;
    
    //Запрос в основную таблицу с временными кондидатами на блокировку
    $query = <<<EOT
        INSERT INTO wp_visiters_bot (`ip`,`browser`,`cnt`,`request`,`input`)
            VALUES  ('{$ip}','{$browser}','{$cnt}','{$request}','$input')
             ON DUPLICATE KEY UPDATE cnt=cnt+1,request=VALUES(request),input=VALUES(input),browser=VALUES(browser)
    EOT;
    
    //Запрос для истории
    $query2 = <<<EOT
        INSERT INTO wp_visiters_bot_history (`ip`,`browser`,`cnt`)
            VALUES  ('{$ip}','{$browser}','{$cnt}')
             ON DUPLICATE KEY UPDATE cnt=cnt+1,browser=VALUES(browser)
    EOT;
    
    
    $wpdb->query($query);
    
    $wpdb->query($query2);
    
    

    L'essence du code est d'obtenir l'adresse IP du visiteur et de l'enregistrer dans la table. Si l'IP existe déjà dans la table, le champ cnt (nombre de requêtes sur le site) sera incrémenté.

  • Maintenant, la partie effrayante... Je vais être brûlé pour mes actions 🙂.
    Pour enregistrer chaque visite sur le site, nous connectons le code du fichier au fichier principal de WordPress — wp-load.php. Oui, nous modifions le fichier de base et c'est précisément après que la variable globale $wpdb existe déjà.

Maintenant, nous pouvons voir à quelle fréquence chaque adresse IP est signalée dans notre tableau et, avec une tasse de café, nous y jetons un coup d'œil toutes les 5 minutes pour comprendre la situation.

Nous identifions les « mauvais » bots potentiels et les bloquons par IP

Ensuite, c'est simple, nous avons copié l'adresse IP « malveillante », ouvert le fichier .htaccess et ajouté à la fin du fichier.

Order allow,deny
Allow from all
# start_auto_deny_list
Deny from 94.242.55.248
# end_auto_deny_list

Voilà, maintenant 94.242.55.248 n'a plus accès au site et ne génère pas de charge sur la base de données.

Mais copier à la main chaque fois n'est pas très judicieux, et de plus, le code a été conçu pour être autonome.

Ajoutons un fichier qui s'exécutera via CRON toutes les 30 minutes :

Code du fichier modifiant .htaccess

get_results("SELECT * FROM wp_visiters_bot WHERE cnt>{$limit_cnt}");

$new_blocked = [];

$exclude_ip = [
    '87.236.16.70' // adresse d'hébergement
];

foreach ($deny_table as $result) {

    if (in_array($result->ip, $exclude_ip)) {
        continue;
    }

    $wpdb->insert('wp_visiters_bot_blocked', ['ip' => $result->ip], ['%s']);
}

$deny_table_blocked = $wpdb->get_results("SELECT * FROM wp_visiters_bot_blocked");

foreach ($deny_table_blocked as $blocked) {
    $new_blocked[] = $blocked->ip;
}

// Nettoyage de la table
$wpdb->query("DELETE FROM wp_visiters_bot");

// echo '
';print_r($new_blocked);echo '';

$file = '.htaccess';

$start_searche_tag = 'start_auto_deny_list';

$end_searche_tag = 'end_auto_deny_list';

$handle = @fopen($file, "r");
if ($handle) {

    $replace_string = ''; // Test pour insérer dans le fichier .htaccess

    $target_content = false; // Drapeau pour la section de code dont nous avons besoin

    while (($buffer = fgets($handle, 4096)) !== false) {

        if (stripos($buffer, 'start_auto_deny_list') !== false) {
            $target_content = true;
            continue;
        }

        if (stripos($buffer, 'end_auto_deny_list') !== false) {
            $target_content = false;

            continue;
        }

        if ($target_content) {
            $replace_string .= $buffer;
        }
    }
    if (!feof($handle)) {
        echo "Erreur : fgets() a échoué de façon inattendue\n";
    }
    fclose($handle);
}

// Fichier .htaccess actuel
$content = file_get_contents($file);

$content = str_replace($replace_string, '', $content);

// Nettoyage de tous les blocages dans le fichier .htaccess
file_put_contents($file, $content);

// Écriture de nouveaux blocages
$str = "# {$start_searche_tag}" . PHP_EOL;

foreach ($new_blocked as $key => $value) {
    $str .= "Deny from {$value}" . PHP_EOL;
}

file_put_contents($file, str_replace("# {$start_searche_tag}", $str, file_get_contents($file)));

Le code du fichier est assez simple et primitif, et son idée principale est de prendre les candidats au blocage et d'inscrire les règles de blocage dans le fichier .htaccess entre les commentaires.
# start_auto_deny_list и # end_auto_deny_list

Maintenant, les IP « malveillantes » se bloquent d'elles-mêmes, et le fichier .htaccess ressemble à ceci :

# BEGIN WordPress
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^index.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]
</IfModule>

# END WordPress

Order allow,deny
Allow from all

# start_auto_deny_list
Deny from 94.242.55.248
Deny from 207.46.13.122
Deny from 66.249.64.164
Deny from 54.209.162.70
Deny from 40.77.167.86
Deny from 54.146.43.69
Deny from 207.46.13.168
....... ниже другие адреса
# end_auto_deny_list

En fin de compte, après l'exécution de ce code, vous pouvez voir le résultat dans le panneau d'hébergement :

Nous identifions les « mauvais » bots potentiels et les bloquons par IP

PS : Ce contenu est original, bien qu'une partie ait été publiée sur mon site, la version sur Habre est plus détaillée.

Source : habr.com

Acheter un hébergement fiable pour les sites avec protection DDoS, serveurs VPS VDS 🔥 Acheter un hébergement fiable pour les sites avec protection DDoS, serveurs VPS VDS | ProHoster