Vypočítáváme potenciální „zlé“ boty a blokujeme je podle IP

Vypočítáváme potenciální „zlé“ boty a blokujeme je podle IP

Dobrý den! V tomto článku vám ukážu, jak mohou uživatelé běžného hostingu odchytit IP adresy, které generují nadměrnou zátěž na web, a poté je zablokovat pomocí nástrojů hostingu, bude tu "malinko" php kódu a několik screenshotů.

Vstupní údaje:

  1. Web vytvořený na CMS WordPress
  2. Hosting Beget (to není reklama, ale screenshoty administrace budou právě tohoto poskytovatele hostingu)
  3. WordPress web byl spuštěn někdy na začátku 2000 a má velké množství článků a materiálů.
  4. Verze PHP 7.2
  5. WP má nejnovější verzi.
  6. Od určité doby web začal generovat vysokou zátěž na MySQL podle údajů hostingu. Každý den tato hodnota překračovala 120% normy pro účet.
  7. Podle Yandex.Metriky web navštěvuje 100-200 lidí denně.

Nejprve bylo provedeno:

  1. Tabulky DB byly vyčištěny od nahromaděného odpadu.
  2. Byly vypnuty nepotřebné pluginy, odstraněny úseky zastaralého kódu.

Pozorně upozorňuji, že byly zkoušeny možnosti kešování (pluginy pro kešování), prováděny pozorování – ale zátěž 120% z jednoho webu zůstávala stále stejná a mohla jen růst.

Takto vypadala přibližná zátěž na databáze hostingu.

Vypočítáváme potenciální „zlé“ boty a blokujeme je podle IP
Na vrcholu je web, o kterém je řeč, o něco níže další weby s tou samou cms a přibližně stejnou návštěvností, ale generují menší zátěž.

Analýza

  • Bylo podniknuto mnoho pokusů s různými variantami kešování dat, prováděna pozorování po dobu několika týdnů (naštěstí mi hosting za tuto dobu ani jednou nenapsal, že jsem tak špatný a že mě odpojí).
  • Byla provedena analýza a hledání pomalých dotazů, poté byla mírně změněna struktura DB a typy tabulek.
  • Pro analýzu byla v první řadě použita zabudovaná AWStats (ten mimochodem pomohl vypočítat nejzlověstnější IP adresu podle objemu provozu.
  • Metrika – metrika dává informace pouze o lidech, a ne o botech.
  • Byly pokusy použít pluginy pro WP, které umí filtrovat a blokovat návštěvníky dokonce podle země pobytu a podle různých kombinací.
  • Zcela radikální způsob, jak na jeden den uzavřít web s označením „Jsme na technické údržbě“ — bylo to také provedeno pomocí slavného pluginu. V tomto případě očekáváme pokles zátěže, ale ne na 0-ové hodnoty, protože ideologie WP se zakládá na hookech a pluginy začínají svou aktivitu, když dojde k nějakému „hooku“, a před jeho nastoupením již mohly být provedeny dotazy do DB.

Nápad

  1. Vypočítat IP adresy, které provádějí mnoho dotazů za krátké časové období.
  2. Zaznamenat počet přístupů na web.
  3. Na základě počtu přístupů zablokovat přístup na web.
  4. Blokovat pomocí záznamu „Deny from“ v souboru .htaccess.
  5. Další možnosti, jako iptables a pravidla pro Nginx, nebudu zvažovat, protože píši o hostingu.

Objevila se myšlenka, takže je třeba ji realizovat, jak jinak…

  • Vytváříme tabulky pro shromažďování dat.
    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='Kandidáti pro blokaci'
    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='Seznam již zablokovaných'
    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='Historie všech dotazů pro debugging'
    COLLATE='utf8_general_ci'
    ENGINE=InnoDB
    AUTO_INCREMENT=1;
    
  • Vytvoříme soubor, do kterého vložíme kód. Kód bude zapisovat do tabulek kandidátů na blokaci a vést historii pro debugging.

    Kód souboru pro zapisování IP adres.

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

    Podstatou kódu je získat IP adresu návštěvníka a zapsat ji do tabulky. Pokud je IP již v tabulce, dojde k navýšení pole cnt (počet dotazů na web).

  • Teď to strašné… Teď mě za mé činy upálí 🙂
    Abychom mohli zapisovat každé přístup na web, připojíme kód souboru do hlavního souboru WordPress — wp-load.php. Ano, skutečně měníme jádro souboru a to až po tom, co již existuje globální proměnná $wpdb.

Takže nyní můžeme vidět, jak často se jednotlivé IP adresy objevují v naší tabulce, a s šálkem kávy se díváme na tuto tabulku každých 5 minut, abychom si udělali obrázek.

Vypočítáváme potenciální „zlé“ boty a blokujeme je podle IP

Dále je to jednoduché, zkopírovali jsme "škodlivou" IP adresu, otevřeli soubor .htaccess a přidali ji na konec souboru.

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

Hotovo, nyní 94.242.55.248 nemá přístup na web a nezatěžuje databázi.

Ale každý záznam kopírovat ručně není příliš spravedlivá činnost, a navíc byl kód zamýšlen jako autonomní.

Přidáme soubor, který se bude spouštět pomocí CRON každých 30 minut:

Kód souboru modifikující .htaccess

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

$new_blocked = [];

$exclude_ip = [
    '87.236.16.70' // adresa hostingu
];

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

// Vyčištění tabulky
$wpdb->query("DELETE FROM wp_visiters_bot");

//$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 pro vložení do souboru .htaccess

    $target_content = false; // Příznak potřebné části kódu

    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 "Chyba: fgets() nečekaně selhalo\n";
    }
    fclose($handle);
}

// Aktuální soubor .htaccess
$content = file_get_contents($file);

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

// Odstraníme všechny blokace v souboru .htaccess
file_put_contents($file, $content);

// Zaznamenání nových blokací
$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)));

Kód souboru je velmi jednoduchý a primitivní, a jeho hlavní myšlenka je vzít kandidáty na blokaci a zapsat blokovací pravidla do souboru .htaccess mezi komentáře.
# start_auto_deny_list и # end_auto_deny_list

Nyní se "škodlivé" IP blokují samy, a soubor .htaccess vypadá přibližně takto:

# 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

Po spuštění takového kódu je možné vidět výsledek v hostingovém panelu:

Vypočítáváme potenciální „zlé“ boty a blokujeme je podle IP

PS: Materiál je autorský, i když jeho část jsem publikoval na svém webu, na Habre vznikla rozšířenější verze.

Zdroj: habr.com

Koupit spolehlivý hosting pro webové stránky s ochranou proti DDoS, VPS VDS servery 🔥 Koupit spolehlivý hosting pro webové stránky s ochranou proti DDoS, VPS VDS servery | ProHoster