We identificeren potentiële ‘kwaadaardige’ bots en blokkeren ze op basis van IP

We identificeren potentiële ‘kwaadaardige’ bots en blokkeren ze op basis van IP

Goededag! In het artikel zal ik je vertellen hoe gebruikers van reguliere hosting IP-adressen kunnen onderscheppen die overmatige belasting op de site genereren en deze vervolgens kunnen blokkeren met behulp van hostingtools, er zal “een klein beetje” php-code zijn, een paar screenshots.

Invoergegevens:

  1. Website gemaakt op CMS WordPress
  2. Hosting Beget (dit is geen advertentie, maar de schermafbeeldingen van het beheerderspaneel zijn afkomstig van deze specifieke hostingprovider)
  3. De WordPress-site is ergens begin 2000 gelanceerd en bevat een groot aantal artikelen en materialen
  4. PHP-versie 7.2
  5. WP heeft de nieuwste versie
  6. Volgens de hostinggegevens begon de site al een tijdje een hoge belasting van MySQL te genereren. Elke dag overschreed deze waarde 120% van de norm per account
  7. Volgens Yandex. De Metrica-site wordt door 100-200 mensen per dag bezocht

Allereerst is dit gedaan:

  1. Databasetabellen werden ontdaan van opgehoopt afval
  2. Onnodige plug-ins zijn uitgeschakeld en delen van verouderde code zijn verwijderd

Tegelijkertijd zou ik uw aandacht willen vestigen op het feit dat caching-opties (caching-plug-ins) werden geprobeerd en observaties werden gedaan - maar de belasting van 120% van één site bleef onveranderd en kon alleen maar toenemen.

Hoe de geschatte belasting van hostingdatabases eruit zag

We identificeren potentiële ‘kwaadaardige’ bots en blokkeren ze op basis van IP
Bovenaan staat de site in kwestie, net daaronder staan ​​andere sites die hetzelfde cms hebben en ongeveer hetzelfde verkeer, maar minder belasting genereren.

Analyse

  • Er zijn veel pogingen gedaan met datacaching-opties, observaties zijn gedurende meerdere weken uitgevoerd (gelukkig heeft de hosting mij gedurende deze tijd nooit geschreven dat ik zo slecht was en dat de verbinding zou worden verbroken)
  • Er was een analyse en zoeken naar langzame zoekopdrachten, waarna de databasestructuur en het tabeltype enigszins werden gewijzigd
  • Voor de analyse gebruikten we voornamelijk de ingebouwde AWStats (die hielpen trouwens om het slechtste IP-adres te berekenen op basis van het verkeersvolume
  • Metriek: de metriek geeft alleen informatie over mensen, niet over bots
  • Er zijn pogingen gedaan om plug-ins voor WP te gebruiken die bezoekers kunnen filteren en blokkeren, zelfs op land van locatie en verschillende combinaties
  • Een volkomen radicale manier bleek om de site een dag te sluiten met de opmerking “We zijn in onderhoud” - dit gebeurde ook met behulp van de beroemde plug-in. In dit geval verwachten we dat de belasting zal afnemen, maar niet tot nulwaarden, aangezien de WP-ideologie gebaseerd is op hooks en plug-ins hun activiteit beginnen wanneer een “hook” optreedt, en voordat de “hook” optreedt, kunnen verzoeken aan de database al gemaakt worden

Idee

  1. Bereken IP-adressen die in korte tijd veel verzoeken doen.
  2. Registreer het aantal hits op de site
  3. Blokkeer de toegang tot de site op basis van het aantal hits
  4. Blokkeer het gebruik van de vermelding "Weigeren van" in het .htaccess-bestand
  5. Ik heb geen andere opties overwogen, zoals iptables en regels voor Nginx, omdat ik over hosting schrijf

Er is een idee verschenen, dus het moet worden geïmplementeerd, want zonder dit...

  • Tabellen maken om gegevens te verzamelen
    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='Кандидаты для блокировки'
    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='Список уже заблокированных'
    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='История всех запросов для дебага'
    COLLATE='utf8_general_ci'
    ENGINE=InnoDB
    AUTO_INCREMENT=1;
    
  • Laten we een bestand maken waarin we de code plaatsen. De code wordt opgenomen in de blokkerende kandidaattabellen en houdt een geschiedenis bij voor foutopsporing.

    Bestandscode voor het vastleggen van IP-adressen

    <?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);
    
    

    De essentie van de code is om het IP-adres van de bezoeker te achterhalen en dit in een tabel te schrijven. Als het ip al in de tabel staat, wordt het cnt-veld vergroot (het aantal verzoeken naar de site)

  • Nu het enge ding... Nu zullen ze me verbranden voor mijn daden :)
    Om elk verzoek aan de site op te nemen, verbinden we de bestandscode met het hoofdbestand van WordPress - wp-load.php. Ja, we veranderen het kernelbestand en precies nadat de globale variabele $wpdb al bestaat

Nu kunnen we dus zien hoe vaak dit of dat IP-adres in onze tabel is gemarkeerd en met een mok koffie kijken we daar elke 5 minuten om het beeld te begrijpen

We identificeren potentiële ‘kwaadaardige’ bots en blokkeren ze op basis van IP

Kopieer vervolgens eenvoudig het “schadelijke” IP-adres, open het .htaccess-bestand en voeg het toe aan het einde van het bestand

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

Dat is alles, nu 94.242.55.248 - heeft geen toegang tot de site en genereert geen belasting van de database

Maar elke keer dat je op deze manier met de hand kopieert, is geen erg rechtvaardige taak, en bovendien was de code bedoeld om autonoom te zijn

Laten we een bestand toevoegen dat elke 30 minuten via CRON wordt uitgevoerd:

Bestandscode die .htaccess wijzigt

<?php

/**
 * Файл автоматического задания блокировок по IP адресу
 * Должен запрашиваться через CRON
 */
if (empty($_REQUEST['key'])) {
    die('Hello');
}

require('wp-load.php');

global $wpdb;

$limit_cnt = 70; //Лимит запросов по которым отбирать

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

$new_blocked = [];

$exclude_ip = [
    '87.236.16.70'//адрес хостинга
];

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;
}

//Очистка таблицы
$wpdb->query("DELETE FROM wp_visiters_bot");

//echo '<pre>';print_r($new_blocked);echo '</pre>';

$file = '.htaccess';

$start_searche_tag = 'start_auto_deny_list';

$end_searche_tag = 'end_auto_deny_list';

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

    $replace_string = '';//Тест для вставки в файл .htaccess

    $target_content = false; //Флаг нужного нам участка кода

    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 "Ошибка: fgets() неожиданно потерпел неудачуn";
    }
    fclose($handle);
}

//Текущий файл .htaccess
$content = file_get_contents($file);

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

//Очищаем все блокировки в файле .htaccess
file_put_contents($file, $content);

//Запись новых блокировок
$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)));

De bestandscode is vrij eenvoudig en primitief en het belangrijkste idee is om kandidaten voor blokkering te nemen en blokkeerregels in het .htaccess-bestand tussen de opmerkingen in te voeren
# start_auto_deny_list en # end_auto_deny_list

Nu worden “schadelijke” IP’s zelf geblokkeerd, en het .htaccess-bestand ziet er ongeveer zo uit:

# 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

Als gevolg hiervan kunt u, nadat deze code begint te werken, het resultaat zien in het hostingpaneel:

We identificeren potentiële ‘kwaadaardige’ bots en blokkeren ze op basis van IP

PS: Het materiaal is van de auteur, hoewel ik een deel ervan op mijn website heb gepubliceerd, heb ik een uitgebreidere versie op Habre gekregen.

Bron: www.habr.com

Voeg een reactie