Identificăm potențialii roboți „răi” și îi blocăm prin IP

Identificăm potențialii roboți „răi” și îi blocăm prin IP

O zi buna! În articol vă voi spune cum utilizatorii de găzduire obișnuită pot prinde adrese IP care generează încărcare excesivă pe site și apoi le blochează folosind instrumente de găzduire, vor exista „un pic” de cod php, câteva capturi de ecran.

Date de intrare:

  1. Site creat pe CMS WordPress
  2. Hosting Beget (aceasta nu este o reclamă, dar capturile de ecran din panoul de administrare vor fi de la acest furnizor de găzduire special)
  3. Site-ul WordPress a fost lansat undeva la începutul anului 2000 și are un număr mare de articole și materiale
  4. PHP versiunea 7.2
  5. WP are cea mai recentă versiune
  6. De ceva timp, site-ul a început să genereze o încărcare mare pe MySQL conform datelor de găzduire. În fiecare zi această valoare a depășit 120% din norma pe cont
  7. Potrivit Yandex. Site-ul Metrica este vizitat de 100-200 de persoane pe zi

În primul rând, s-a făcut acest lucru:

  1. Tabelele bazelor de date au fost curățate de gunoiul acumulat
  2. Pluginurile inutile au fost dezactivate, secțiunile de cod învechit au fost eliminate

În același timp, aș dori să vă atrag atenția asupra faptului că s-au încercat opțiunile de caching (plugin-uri de caching), s-au făcut observații - dar încărcarea de 120% de la un site a fost neschimbată și a putut doar să crească.

Cum arăta încărcarea aproximativă a bazelor de date de găzduire

Identificăm potențialii roboți „răi” și îi blocăm prin IP
În partea de sus este site-ul în cauză, chiar mai jos sunt alte site-uri care au aceleași cms și aproximativ același trafic, dar creează mai puțină încărcare.

analiza

  • S-au făcut multe încercări cu opțiuni de stocare în cache a datelor, s-au efectuat observații pe parcursul mai multor săptămâni (din fericire, în acest timp, hostingul nu mi-a scris niciodată că sunt atât de rău și că voi fi deconectat)
  • A existat o analiză și o căutare pentru interogări lente, apoi structura bazei de date și tipul de tabel au fost ușor modificate
  • Pentru analiză, am folosit în primul rând AWStats încorporat (apropo, a ajutat la calcularea celei mai proaste adrese IP pe baza volumului de trafic
  • Valoare - metrica furnizează informații numai despre oameni, nu despre roboți
  • Au existat încercări de a folosi pluginuri pentru WP care pot filtra și bloca vizitatorii chiar și după țara de locație și diverse combinații
  • O modalitate complet radicală s-a dovedit a fi închiderea site-ului pentru o zi cu nota „Suntem în întreținere” - acest lucru a fost făcut și folosind celebrul plugin. În acest caz, ne așteptăm ca sarcina să scadă, dar nu la valori zero, deoarece ideologia WP se bazează pe cârlige și plugin-urile își încep activitatea atunci când apare un „cârlig”, iar înainte de apariția „cârligului”, solicitările către baza de date pot să fie deja făcute

Idee

  1. Calculați adrese IP care fac multe solicitări într-o perioadă scurtă de timp.
  2. Înregistrați numărul de accesări pe site
  3. Blocați accesul la site în funcție de numărul de accesări
  4. Blocați folosind intrarea „Refuzați de la” din fișierul .htaccess
  5. Nu am luat în considerare alte opțiuni, cum ar fi iptables și reguli pentru Nginx, pentru că scriu despre găzduire

A apărut o idee, așa că trebuie implementată, ca și fără aceasta...

  • Crearea de tabele pentru a acumula date
    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;
    
  • Să creăm un fișier în care vom plasa codul. Codul va înregistra în tabelele candidate de blocare și va păstra un istoric pentru depanare.

    Cod de fișier pentru înregistrarea adreselor 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);
    
    

    Esența codului este de a obține adresa IP a vizitatorului și de a o scrie într-un tabel. Dacă ip-ul este deja în tabel, câmpul cnt va fi mărit (numărul de solicitări către site)

  • Acum chestia înfricoșătoare... Acum mă vor arde pentru acțiunile mele :)
    Pentru a înregistra fiecare solicitare pe site, conectăm codul fișierului la fișierul principal WordPress - wp-load.php. Da, schimbăm fișierul kernel și tocmai după ce variabila globală $wpdb există deja

Așadar, acum putem vedea cât de des este marcată cutare sau cutare adresă IP în tabelul nostru și cu o cană de cafea ne uităm acolo o dată la 5 minute pentru a înțelege imaginea

Identificăm potențialii roboți „răi” și îi blocăm prin IP

Apoi pur și simplu copiați IP-ul „dăunător”, deschideți fișierul .htaccess și adăugați-l la sfârșitul fișierului

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

Gata, acum 94.242.55.248 - nu are acces la site și nu generează încărcare pe baza de date

Dar de fiecare dată când copiați manual astfel nu este o sarcină foarte corectă și, în plus, codul era menit să fie autonom

Să adăugăm un fișier care va fi executat prin CRON la fiecare 30 de minute:

Codul fișierului modificând .htaccess

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

Codul fișierului este destul de simplu și primitiv și ideea sa principală este de a lua candidații pentru blocare și de a introduce reguli de blocare în fișierul .htaccess între comentarii
# start_auto_deny_list și # end_auto_deny_list

Acum, IP-urile „dăunătoare” sunt blocate de la sine, iar fișierul .htaccess arată cam așa:

# 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

Ca urmare, după ce acest cod începe să funcționeze, puteți vedea rezultatul în panoul de găzduire:

Identificăm potențialii roboți „răi” și îi blocăm prin IP

PS: Materialul este al autorului, deși am publicat o parte din el pe site-ul meu, am primit o versiune mai extinsă pe Habre.

Sursa: www.habr.com

Adauga un comentariu