Rileviamo potenziali bot maligni e li blocchiamo per IP

Rileviamo potenziali bot maligni e li blocchiamo per IP

Buongiorno! In questo articolo parlerò di come gli utenti di hosting condiviso possono identificare gli indirizzi IP che generano un eccessivo carico sul sito e bloccarli utilizzando gli strumenti di hosting; ci sarà un po' di codice PHP e alcuni screenshot.

Dati in ingresso:

  1. Sito creato con CMS WordPress
  2. Hosting Beget (non è pubblicità, ma gli screenshot dell'admin saranno di questo fornitore di hosting)
  3. Il sito WordPress è stato attivato intorno ai primi anni 2000 e ha un grande numero di articoli e materiali
  4. Versione PHP 7.2
  5. WP ha l'ultima versione
  6. Per un certo periodo, il sito ha iniziato a generare un alto carico su MySQL secondo i dati dell'hosting. Ogni giorno questo valore superava il 120% della norma per l'account
  7. Secondo Yandex.Metrica, il sito viene visitato da 100-200 persone al giorno

Nella prima fase sono state effettuate le seguenti operazioni:

  1. Ripulite le tabelle del DB dai rifiuti accumulati
  2. disattivati i plugin non necessari, rimossi i pezzi di codice obsoleto

Nel contempo, sottolineo che sono state provate diverse opzioni di caching (plugin di caching), sono stati effettuati monitoraggi — ma il carico del 120% da un solo sito è rimasto invariato e poteva solo aumentare.

Come si presentava il carico dei database dell'hosting

Rileviamo potenziali bot maligni e li blocchiamo per IP
In cima c'è il sito di cui si parla, un po' più in basso ci sono altri siti che utilizzano la stessa CMS e hanno un traffico simile, ma generano meno carico.

Analisi

  • Sono stati fatti molti tentativi con opzioni di caching dei dati, e sono state effettuate osservazioni per diverse settimane (fortunatamente, durante questo periodo l'hosting non mi ha mai avvisato di essere un cattivo utente e di essere rischiando di essere disconnesso)
  • È stata effettuata un'analisi alla ricerca di query lente, dopodiché è stata leggermente modificata la struttura del database e il tipo di tabelle
  • Per l'analisi è stato utilizzato principalmente AWStats integrato (che tra l'altro ha aiutato a identificare l'IP più malevolo in base al volume di traffico)
  • La metrica — la metrica fornisce informazioni solo sulle persone, non sui bot
  • Ci sono stati tentativi di utilizzare plugin per WP che possono filtrare e bloccare i visitatori anche in base al paese di origine e a varie combinazioni
  • Un modo davvero radicale per chiudere il sito per un giorno con la dicitura "In manutenzione" è stato realizzato anche grazie al famoso plugin. In questo caso, il carico previsto è diminuito, ma non a valori nulli, poiché l'ideologia di WP si basa su hook e i plugin iniziano la loro attività al verificarsi di un "hook", mentre fino all'attivazione dell'"hook" possono già essere state fatte richieste al DB.

Idea

  1. Calcolare gli indirizzi IP che effettuano molte richieste in un breve periodo di tempo.
  2. Registrare il numero di accessi al sito.
  3. Bloccate l'accesso al sito in base al numero di accessi.
  4. Bloccare attraverso la registrazione "Deny from" nel file .htaccess.
  5. Altre opzioni, come iptables e regole per Nginx, non sono considerate, poiché scrivo di hosting.

Se è nata un'idea, bisogna realizzarla, come si può fare diversamente...

  • Creiamo le tabelle per l'accumulo dei 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='Candidati per il blocco'
    COLLATE='utf8_general_ci'
    ENGINE=InnoDB
    AUTO_INCREMENT=1;
    

    CREA LA TABELLA `wp_visiters_bot_blocked` (
    	`id` INT(11) NON NULL AUTO_INCREMENT,
    	`ip` VARCHAR(300) NON NULL,
    	`data_update` DATETIME NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    	PRIMARY KEY (`id`),
    	UNIQUE INDEX `ip` (`ip`)
    )
    COMMENT='Elenco degli indirizzi bloccati'
    COLLATE='utf8_general_ci'
    ENGINE=InnoDB
    AUTO_INCREMENT=59;
    

    CREA LA TABELLA `wp_visiters_bot_history` (
    	`id` INT(11) NON 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='Storia di tutte le richieste per il debug'
    COLLATE='utf8_general_ci'
    ENGINE=InnoDB
    AUTO_INCREMENT=1;
    
  • Creeremo un file in cui inseriremo il codice. Il codice registrerà nelle tabelle i candidati per il blocco e terrà traccia della storia per il debug.

    Codice del file per registrare gli 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);
    
    

    Il codice serve a ottenere l'indirizzo IP del visitatore e a registrarlo nella tabella. Se l'IP è già presente nella tabella, verrà incrementato il campo cnt (numero di richieste al sito)

  • Ora la parte spaventosa… Adesso mi bruceranno per le mie azioni 🙂
    Per registrare ogni accesso al sito, collegate il codice del file nel file principale di WordPress — wp-load.php. Sì, stiamo modificando il file principale e proprio dopo che esiste già la variabile globale $wpdb

Ora possiamo vedere con quale frequenza un determinato indirizzo IP viene segnato nella nostra tabella e con una tazza di caffè ci diamo un'occhiata ogni 5 minuti per capire la situazione

Rileviamo potenziali bot maligni e li blocchiamo per IP

Successivamente, basta copiare l'IP 'maligno', aprire il file .htaccess e aggiungere alla fine del file

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

Ora 94.242.55.248 non ha accesso al sito e non genera carico sul database

Ma non è molto pratico copiare manualmente ogni volta e, inoltre, il codice è stato pensato per essere autonomo

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

Codice del file che modifica .htaccess

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

$new_blocked = [];

$exclude_ip = [
    '87.236.16.70' // indirizzo dell'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;
}

// Pulizia della tabella
$wpdb->query("DELETE FROM wp_visiters_bot");

//echo '
';print_r($new_blocked);echo '
';$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 per l'inserimento nel file .htaccess$target_content = false; // Flag per la sezione del codice desideratawhile (($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 "Errore: fgets() ha inaspettatamente fallito n"; } fclose($handle); }// Contenuto corrente del file .htaccess $content = file_get_contents($file);$content = str_replace($replace_string, '', $content);// Rimuoviamo tutti i blocchi dal file .htaccess file_put_contents($file, $content);// Scrittura dei nuovi blocchi $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 è piuttosto semplice e primitivo. La sua idea principale è quella di prendere gli indirizzi IP da bloccare e scrivere le regole di blocco nel file .htaccess tra i commenti.
# start_auto_deny_list и # end_auto_deny_list

Ora gli IP "nocivi" vengono bloccati automaticamente e il file .htaccess appare più o meno così:

# 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

Dopo l'attivazione di questo codice, puoi vedere i risultati nel pannello di hosting:

Rileviamo potenziali bot maligni e li blocchiamo per IP

PS: Questo materiale è originale; anche se ne ho pubblicato una parte sul mio sito, su Habre è stata creata una versione più estesa.

Fonte: habr.com

Acquista hosting affidabile per siti web con protezione DDoS, VPS VDS server 🔥 Acquista hosting affidabile per siti web con protezione DDoS, VPS VDS server | ProHoster