Tuvastame potentsiaalsed "kurjad" robotid ja blokeerime need IP järgi

Tuvastame potentsiaalsed "kurjad" robotid ja blokeerime need IP järgi

Head päeva! Artiklis räägin teile, kuidas tavalise hostimise kasutajad saavad saidil liigset koormust tekitavaid IP-aadresse kinni püüda ja seejärel hostimistööriistade abil blokeerida, ilmub "natuke" php-koodi, paar ekraanipilti.

Sisendandmed:

  1. Veebileht loodud CMS WordPressis
  2. Begeti hostimine (see ei ole reklaam, vaid administraatoripaneeli ekraanipildid pärinevad sellelt konkreetselt hostiteenuse pakkujalt)
  3. WordPressi sait käivitati kuskil 2000. aasta alguses ning sellel on palju artikleid ja materjale
  4. PHP versioon 7.2
  5. WP-l on uusim versioon
  6. Juba mõnda aega hakkas sait hostimisandmete põhjal MySQL-ile suurt koormust tekitama. Iga päev ületas see väärtus 120% konto normist
  7. Yandexi andmetel. Metrica saiti külastab 100-200 inimest päevas

Esiteks tehti seda:

  1. Andmebaasi tabelid puhastati kogunenud prügist
  2. Mittevajalikud pistikprogrammid keelati, aegunud koodi osad eemaldati

Samas juhin teie tähelepanu asjaolule, et prooviti vahemällu salvestamise võimalusi (vahemällu salvestamise pluginaid), tehti vaatlusi - kuid 120% koormus ühelt saidilt jäi muutumatuks ja sai ainult kasvada.

Kuidas nägi välja ligikaudne andmebaaside hostimise koormus

Tuvastame potentsiaalsed "kurjad" robotid ja blokeerime need IP järgi
Ülaosas on kõnealune sait, veidi all teised saidid, millel on samad cm-d ja ligikaudu sama liiklus, kuid mis tekitavad vähem koormust.

Analüüs

  • Andmete vahemällu salvestamise võimalustega tehti palju katseid, vaatlusi tehti mitme nädala jooksul (õnneks ei kirjutanud hostimine mulle selle aja jooksul kordagi, et mul on nii halb ja ühendus katkeb)
  • Toimus aeglaste päringute analüüs ja otsing, seejärel muudeti veidi andmebaasi struktuuri ja tabelitüüpi
  • Analüüsiks kasutasime eelkõige sisseehitatud AWStatsi (see aitas muuseas liiklusmahu põhjal arvutada halvima IP-aadressi
  • Mõõdik – mõõdik annab teavet ainult inimeste, mitte robotite kohta
  • WP jaoks on üritatud kasutada pluginaid, mis suudavad külastajaid filtreerida ja blokeerida isegi asukohariigi ja erinevate kombinatsioonide järgi
  • Täiesti radikaalseks viisiks osutus saidi päevaks sulgemine märkega “Oleme hoolduses” - seda tehti ka kuulsa pistikprogrammi abil. Sel juhul eeldame, et koormus langeb, kuid mitte nullväärtustele, kuna WP ideoloogia põhineb konksudel ja pistikprogrammid alustavad oma tegevust „konksu” ilmnemisel ning enne „konksu” tekkimist saavad päringud andmebaasile juba tehtud

Mõte

  1. Arvutage IP-aadressid, mis esitavad lühikese aja jooksul palju päringuid.
  2. Salvestage saidi tabamuste arv
  3. Blokeeri juurdepääs saidile tabamuste arvu alusel
  4. Blokeerige, kasutades .htaccess-failis kirjet „Keela
  5. Ma ei kaalunud muid võimalusi, nagu iptables ja Nginxi reeglid, sest kirjutan hostimisest

Idee on ilmunud, nii et see tuleb ellu viia, kuna ilma selleta ...

  • Tabelite loomine andmete kogumiseks
    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;
    
  • Loome faili, kuhu asetame koodi. Kood salvestatakse blokeerimiskandidaatide tabelitesse ja säilitab silumiseks ajaloo.

    Failikood IP-aadresside salvestamiseks

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

    Koodi põhiolemus on hankida külastaja IP-aadress ja kirjutada see tabelisse. Kui ip on juba tabelis, suurendatakse cnt välja (saidile saadetavate päringute arv)

  • Nüüd see hirmus asi... Nüüd põletavad nad mu tegude pärast ära :)
    Iga saidi päringu salvestamiseks ühendame faili koodi WordPressi põhifailiga - wp-load.php. Jah, me muudame kerneli faili ja täpselt pärast seda, kui globaalne muutuja $wpdb on juba olemas

Nii, nüüd näeme, kui sageli see või teine ​​IP-aadress meie tabelisse märgitud on ja kohvitassiga vaatame korra 5 minuti tagant sinna, et pildist aru saada

Tuvastame potentsiaalsed "kurjad" robotid ja blokeerime need IP järgi

Seejärel kopeerige lihtsalt "kahjulik" IP, avage .htaccess-fail ja lisage see faili lõppu

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

See on kõik, nüüd 94.242.55.248 - tal pole saidile juurdepääsu ja see ei koorma andmebaasi

Kuid iga kord niisugune käsitsi kopeerimine ei ole kuigi õige ülesanne ja pealegi oli kood mõeldud autonoomseks

Lisame faili, mis käivitatakse CRON-i kaudu iga 30 minuti järel:

Failikood, mis muudab .htaccessi

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

Faili kood on üsna lihtne ja primitiivne ning selle põhiidee on võtta blokeerimise kandidaadid ja sisestada blokeerimisreeglid .htaccess faili kommentaaride vahele
# start_auto_deny_list ja # end_auto_deny_list

Nüüd on "kahjulikud" IP-d iseenesest blokeeritud ja .htaccess-fail näeb välja umbes selline:

# 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

Selle tulemusel näete tulemust pärast selle koodi tööle hakkamist hostimispaneelil:

Tuvastame potentsiaalsed "kurjad" robotid ja blokeerime need IP järgi

PS: materjal on autori oma, kuigi avaldasin osa sellest oma veebisaidil, sain Habre kohta laiendatud versiooni.

Allikas: www.habr.com

Lisa kommentaar