Изчисляваме потенциалните „зли“ ботове и ги блокираме по IP

Изчисляваме потенциалните „зли“ ботове и ги блокираме по IP

Добър ден! В статията ще обясня как потребителите на обикновен хостинг могат да ловят IP адресите, които генерират прекомерно натоварване на сайта и след това да ги блокират с средства от хостинга, ще има малко php код и няколко скрийншота.

Входни данни:

  1. Сайт, създаден на CMS WordPress
  2. Хостинг Бегет (това не е реклама, но екраните на администрацията ще бъдат именно на този хостинг доставчик)
  3. WordPress сайтът е стартиран около началото на 2000-те и има голямо количество статии и материали
  4. Версия на PHP 7.2
  5. WP има последната версия
  6. От известно време сайтът започна да генерира високо натоварване на MySQL според данните на хостинга. Всеки ден това значение надвишава 120% от нормата за акаунта
  7. Според Яндекс. Метрика сайтът посещава 100-200 души на ден

Първо бяха предприети следните действия:

  1. Почистени таблиците на БД от натрупаното смет
  2. Деактивирани ненужни приставки, премахнати участъци от остарял код

Също така, обръщам внимание, опитвахме варианти за кеширане (приставки за кеширане), провеждаха се наблюдения - но натоварването от 120% от един сайт оставаше неизменно и можеше само да нараства.

Ето как изглеждаше приблизителното натоварване по бази данни на хостинга

Изчисляваме потенциалните „зли“ ботове и ги блокираме по IP
На първо място е сайтът, за който става въпрос, малко по-долу са други сайтове, които имат същата cms и приблизително такова посещение, но генерират по-малко натоварване.

Анализ

  • Бяха предприети много опити с варианти за кеширане на данните, провеждани наблюдения в течение на няколко седмици (добре, хостингът през това време никога не ми е казал, че съм толкова лош и ще ме изключат)
  • Беше проведен анализ и търсене на бавни заявки, след което структурата на БД и типът на таблиците бяха малко променени
  • За анализа основно беше използван вграден AWStats (той всъщност помогна да се определи най-лошият IP адрес по обем на трафика
  • Метрика - метриката предоставя информация само за хора, а не за ботове
  • Имаше опити да се използват приставки за WP, които могат да филтрират и блокират посетители дори по държава на пребиваване и по различни комбинации
  • Напълно радикалният начин за затваряне на сайта за 24 часа с етикет "Ние сме в техническо обслужване" се оказа изграждането чрез известния плъгин. В този случай натоварването, което очаквахме, спадна, но не до нулеви стойности, тъй като идеологията на WP се основава на хукове и плъгините започват своята активност, когато настъпи някакъв "хук", а преди да настъпи "хук" може вече да са направени заявки към БД.

Идеята

  1. Да се изчислят IP адресите, които правят много заявки за кратък времеви интервал.
  2. Да се запише броят на обращенията към сайта.
  3. На базата на броя на обращенията да се блокира достъпът до сайта.
  4. Да се блокира с помощта на записа "Deny from" в файла .htaccess.
  5. Други варианти, като iptables и правила за Nginx не разглеждам, тъй като пиша за хостинг.

Появи се идея, следователно трябва да се реализира, как без това...

  • Създаваме таблици за натрупване на данни.
    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;
    
  • Създаваме файл, в който ще поставим кода. Кодът ще записва в таблиците на кандидатите за блокировка и ще води история за дебъгване.

    Код на файла за записване на 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);
    
    

    Същността на кода е да получи IP адреса на посетителя и да го запише в таблицата. Ако IP адресът вече съществува в таблицата, полето cnt (броя на заявките към сайта) ще се увеличи.

  • Сега нещо страшно... Сега ще ме изгорят за действията ми 🙂
    За да запишем всяко посещение на сайта, свързваме кода на файла в главния файл на WordPress — wp-load.php. Да, наистина променяме ядрото на файла и точно след като вече съществува глобалната променлива $wpdb.

Сега можем да видим как често даден IP адрес се отбелязва в нашата таблица и с чаша кафе надникваме там на всеки 5 минути, за да разберем картината.

Изчисляваме потенциалните „зли“ ботове и ги блокираме по IP

Сега просто копираме "вредния" IP, отваряме файла .htaccess и добавяме в края на файла.

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

Вече 94.242.55.248 няма достъп до сайта и не генерира натоварване на базата данни.

Но всеки път да копираме ръчно не е много удобно, а и кодът е замислен като автономен.

Добавяме файл, който ще се изпълнява по CRON на всеки 30 минути:

Код на файла, който модифицира .htaccess

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 '
';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 = ''; //Тест за вставка в файла .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() неочаквано не успя"; } 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)));

Кодът на файла е достатъчно прост и примитивен, а основната идея е да вземем кандидатите за блокиране и да запишем правилата за блокировка във файла .htaccess между коментарите.
# start_auto_deny_list и # end_auto_deny_list

Сега "вредните" IP адреси се блокират автоматично, а файлът .htaccess изглежда приблизително така:

# 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

В крайна сметка, след стартирането на този код, можете да видите резултата в хостинг панела:

Изчисляваме потенциалните „зли“ ботове и ги блокираме по IP

PS: Материалът е авторски, въпреки че част от него публикувах на сайта си, но на Habre се получи по-разширена версия.

Източник: habr.com

Купете надежден хостинг за сайтове със защита от DDoS, VPS и VDS сървъри 🔥 Купете надежден хостинг за сайтове със защита от DDoS, VPS и VDS сървъри | ProHoster