Identifichiamo potenziali bot “malvagi” e li blocchiamo tramite IP

Identifichiamo potenziali bot “malvagi” e li blocchiamo tramite IP

Buona giornata! Nell'articolo ti spiegherò come gli utenti di un hosting regolare possono individuare gli indirizzi IP che generano un carico eccessivo sul sito e quindi bloccarli utilizzando gli strumenti di hosting, ci sarà “un po'” di codice php, alcuni screenshot.

Dati in ingresso:

  1. Sito realizzato su CMS WordPress
  2. Hosting Beget (questa non è una pubblicità, ma gli screenshot del pannello di amministrazione proverranno da questo particolare provider di hosting)
  3. Il sito WordPress è stato lanciato da qualche parte all'inizio degli anni 2000 e contiene un gran numero di articoli e materiali
  4. PHP versione 7.2
  5. WP ha l'ultima versione
  6. Da qualche tempo il sito ha iniziato a generare un carico elevato su MySQL secondo i dati di hosting. Ogni giorno questo valore superava il 120% della norma per account
  7. Secondo Yandex. Il sito Metrica è visitato da 100-200 persone al giorno

Innanzitutto è stato fatto questo:

  1. Le tabelle del database sono state ripulite dalla spazzatura accumulata
  2. I plugin non necessari sono stati disabilitati, sezioni di codice obsoleto sono state rimosse

Allo stesso tempo, vorrei attirare la vostra attenzione sul fatto che sono state provate le opzioni di memorizzazione nella cache (plug-in di memorizzazione nella cache), sono state fatte osservazioni, ma il carico del 120% da un sito è rimasto invariato e poteva solo aumentare.

Qual era il carico approssimativo sui database di hosting

Identifichiamo potenziali bot “malvagi” e li blocchiamo tramite IP
In alto c'è il sito in questione, appena sotto ci sono altri siti che hanno gli stessi cms e approssimativamente lo stesso traffico, ma creano meno carico.

analisi

  • Sono stati fatti molti tentativi con le opzioni di caching dei dati, le osservazioni sono state effettuate per diverse settimane (per fortuna in questo periodo l'hosting non mi ha mai scritto che stavo così male e che sarei stato disconnesso)
  • È stata effettuata un'analisi e una ricerca di query lente, quindi la struttura del database e il tipo di tabella sono stati leggermente modificati
  • Per l'analisi, abbiamo utilizzato principalmente AWStats integrati (a proposito, ci ha aiutato a calcolare l'indirizzo IP peggiore in base al volume di traffico
  • Metrica: la metrica fornisce informazioni solo sulle persone, non sui bot
  • Ci sono stati tentativi di utilizzare plugin per WP in grado di filtrare e bloccare i visitatori anche per paese di ubicazione e varie combinazioni
  • Un modo completamente radicale si è rivelato quello di chiudere il sito per un giorno con la nota "Siamo in manutenzione" - anche questo è stato fatto utilizzando il famoso plugin. In questo caso, ci aspettiamo che il carico scenda, ma non a valori pari a zero, poiché l'ideologia WP è basata su hook e i plugin iniziano la loro attività quando si verifica un "hook", e prima che si verifichi l'"hook", le richieste al database possono già essere realizzato

Idea

  1. Calcola gli indirizzi IP che effettuano molte richieste in un breve periodo di tempo.
  2. Registra il numero di visite al sito
  3. Blocca l'accesso al sito in base al numero di visite
  4. Blocca utilizzando la voce "Nega da" nel file .htaccess
  5. Non ho considerato altre opzioni, come iptables e le regole per Nginx, perché sto scrivendo di hosting

È apparsa un'idea, quindi deve essere implementata, poiché senza questa...

  • Creazione di tabelle per accumulare dati
    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;
    
  • Creiamo un file in cui inseriremo il codice. Il codice verrà registrato nelle tabelle dei candidati al blocco e manterrà una cronologia per il debug.

    Codice file per la registrazione degli indirizzi 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);
    
    

    L'essenza del codice è ottenere l'indirizzo IP del visitatore e scriverlo in una tabella. Se l'ip è già in tabella verrà incrementato il campo cnt (il numero di richieste al sito)

  • Ora la cosa spaventosa... Ora mi bruceranno per le mie azioni :)
    Per registrare ogni richiesta al sito, colleghiamo il codice del file al file principale di WordPress: wp-load.php. Sì, cambiamo il file del kernel e proprio dopo esiste già la variabile globale $wpdb

Quindi ora possiamo vedere quanto spesso questo o quell'indirizzo IP è segnato nella nostra tabella e con una tazza di caffè guardiamo lì una volta ogni 5 minuti per capire l'immagine

Identifichiamo potenziali bot “malvagi” e li blocchiamo tramite IP

Quindi è sufficiente copiare l'IP “dannoso”, aprire il file .htaccess e aggiungerlo alla fine del file

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

Questo è tutto, ora 94.242.55.248 - non ha accesso al sito e non genera carico sul database

Ma ogni volta copiare a mano in questo modo non è un compito molto giusto, e inoltre il codice doveva essere autonomo

Aggiungiamo un file che verrà eseguito tramite CRON ogni 30 minuti:

Codice file che modifica .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)));

Il codice del file è abbastanza semplice e primitivo e la sua idea principale è quella di prendere i candidati per il blocco e inserire le regole di blocco nel file .htaccess tra i commenti
# start_auto_deny_list e # end_auto_deny_list

Ora gli IP "dannosi" vengono bloccati da soli e il file .htaccess assomiglia a questo:

# 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

Di conseguenza, dopo che questo codice inizia a funzionare, puoi vedere il risultato nel pannello di hosting:

Identifichiamo potenziali bot “malvagi” e li blocchiamo tramite IP

PS: Il materiale è dell'autore, anche se ne ho pubblicato una parte sul mio sito, ho ottenuto una versione più ampliata su Habre.

Fonte: habr.com

Aggiungi un commento