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

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

Bună ziua! În acest articol voi explica cum utilizatorii de hosting obișnuit pot captura adresele IP care generează o încărcare excesivă a site-ului și apoi le pot bloca folosind mijloacele de găzduire; va fi un pic de cod PHP și câteva capturi de ecran.

Datele inițiale:

  1. Site-ul creat pe CMS WordPress
  2. Găzduire Beget (nu este reclamă, dar capturile de ecran ale panoului de administrare vor fi exact de la acest furnizor de hosting)
  3. Site-ul WordPress a fost lansat undeva la începutul anilor 2000 și are un număr mare de articole și materiale.
  4. Versiunea PHP 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 depășea 120% din norma pentru cont.
  7. Conform Yandex.Metrica, site-ul primește între 100-200 de vizitatori pe zi.

În primul rând, s-a realizat:

  1. Curățarea tabelelor Bazei de Date de gunoi acumulate.
  2. Dezactivarea pluginurilor inutile și eliminarea porțiunilor de cod învechit.

Menționez că au fost testate opțiuni de cache (pluginuri de caching) și au fost efectuate observații — dar încărcarea de 120% de la un singur site a fost constantă și putea doar să crească.

Așa arăta încărcarea aproximativă pe bazele de date ale găzduirii.

Identificăm potențialii „răi” roboți și îi blocăm după IP
În frunte se află site-ul despre care se discută, puțin mai jos sunt alte site-uri care au aceeași CMS și aproximativ aceeași vizibilitate, dar generează o încărcare mai mică.

Analiză

  • Au fost multe încercări cu varianta caching-ului de date, iar observațiile s-au desfășurat pe parcursul a câtorva săptămâni (din fericire, găzduirea în această perioadă nu mi-a spus niciodată că sunt atât de rău încât mă vor deconecta).
  • A avut loc o analiză și căutare a interogărilor lente, apoi s-a modificat puțin structura Bazei de Date și tipul tabelelor.
  • Pentru analiză, s-a folosit în primul rând AWStats încorporat (de altfel, m-a ajutat să identific cel mai problematic IP în funcție de trafic).
  • Metrica — metrica oferă informații doar despre oameni, nu despre boti.
  • Au fost încercări de a folosi pluginuri pentru WP, care pot filtra și bloca vizitatorii chiar și în funcție de țara în care se află și diverse combinații.
  • O modalitate radicală de a închide site-ul timp de o zi cu mențiunea „Suntem în întreținere” este să folosești un plugin cunoscut. În acest caz, încărcarea ar trebui să scadă, dar nu până la valori de 0, deoarece ideologia WP se bazează pe hooks, iar plugin-urile își încep activitatea la apariția unui „hook”, iar până la această apariție pot fi deja făcute cereri către baza de date.

Ideea

  1. Calculați adresele IP care trimit multe cereri într-un interval scurt de timp.
  2. Fixează numărul de accesări ale site-ului.
  3. Pe baza numărului de accesări, blocați accesul la site.
  4. Blocați prin înregistrarea „Deny from” în fișierul .htaccess.
  5. Alte variante, cum ar fi iptables și regulile pentru Nginx, nu sunt luate în considerare, deoarece scriu despre găzduire.

A apărut ideea, deci trebuie implementată, cum altfel...

  • Creăm tabele pentru acumularea datelor.
    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='Candidați pentru blocare'
    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='Lista deja blocată'
    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='Istoricul tuturor cererilor pentru debugging'
    COLLATE='utf8_general_ci'
    ENGINE=InnoDB
    AUTO_INCREMENT=1;
    
  • Vom crea un fișier în care vom plasa codul. Codul va înregistra în tabelele candidaților pentru blocare și va conduce istoricul pentru debugging.

    Codul fișierului, 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);
    
    

    Ideea codului este să obțină adresa IP a vizitatorului și să o înregistreze în tabel. Dacă IP-ul există deja în tabel, se va crește câmpul cnt (numărul de cereri către site).

  • Acum urmează partea dificilă… Acum mă vor arde pentru acțiunile mele 🙂
    Pentru a înregistra fiecare accesare a site-ului, conectăm codul fișierului în fișierul principal WordPress — wp-load.php. Da, exact astfel modificăm fișierul de bază și doar după ce variabila globală $wpdb a fost deja creată.

Așadar, acum putem vedea frecvența cu care o anumită adresă IP este menționată în tabelul nostru și, cu o cană de cafea, ne uităm acolo la fiecare 5 minute pentru a înțelege situația.

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

Apoi, este simplu, am copiat adresa IP „dăunătoare”, am deschis fișierul .htaccess și am adăugat-o la finalul fișierului.

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

Totul, acum 94.242.55.248 nu mai are acces la site și nu generează o încărcare pe baza de date.

Dar a copia manual de fiecare dată nu este o activitate prea corectă, iar codul a fost gândit ca fiind autonom.

Vom adăuga un fișier care se va executa prin CRON la fiecare 30 de minute:

Codul fișierului care modifică .htaccess

<?php

/**
 * Fișierul pentru sarcina automată de blocare pe baza adresei IP
 * Trebuie să fie apelat prin CRON
 * / 
if (empty($_REQUEST['key'])) {
    die('Hello');
}

require('wp-load.php');

global $wpdb;

$limit_cnt = 70; // Limita cererilor pentru selectare

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

$new_blocked = [];

$exclude_ip = [
    '87.236.16.70' // adresa de hosting
];

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

// Curățarea tabelului
$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 = ''; // Test pentru inserarea în fișierul .htaccess

    $target_content = false; // Flaga pentru secțiunea de cod dorită

    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 "Eroare: fgets() a eșuat în mod neașteptatn";
    }
    fclose($handle);
}

// Fișierul curent .htaccess
$content = file_get_contents($file);

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

// Curățăm toate blocările din fișierul .htaccess
file_put_contents($file, $content);

// Scrierea noilor blocări
$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, iar ideea sa principală este de a lua candidații pentru blocare și de a scrie regulile de blocare în fișierul .htaccess între comentarii.
# start_auto_deny_list и # end_auto_deny_list

Acum, adresele IP „dăunătoare” se blochează singure, 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

În cele din urmă, după activarea acestui cod, se poate observa rezultatul în panoul de control al găzduirii:

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

PS: Materialul este original, deși o parte din acesta am publicat-o pe site-ul meu, dar pe Habra a ieșit o versiune mai extinsă.

Sursa: habr.com

Cumpără un hosting fiabil pentru site-uri cu protecție DDoS, servere VPS VDS 🔥 Cumpără un hosting fiabil pentru site-uri cu protecție DDoS, servere VPS VDS | ProHoster