Identifiquem potencials robots "malvats" i els bloquegem per IP

Identifiquem potencials robots "malvats" i els bloquegem per IP

Bon dia! A l'article us explicaré com els usuaris d'allotjament habitual poden capturar adreces IP que generen una càrrega excessiva al lloc i després bloquejar-les mitjançant eines d'allotjament, hi haurà "una mica" de codi php, unes quantes captures de pantalla.

Dades d'entrada:

  1. Lloc web creat en CMS WordPress
  2. Hosting Beget (no és un anunci, però les captures de pantalla del tauler d'administració seran d'aquest proveïdor d'allotjament en particular)
  3. El lloc de WordPress es va llançar a principis de l'any 2000 i té un gran nombre d'articles i materials
  4. PHP versió 7.2
  5. WP té l'última versió
  6. Des de fa un temps, el lloc va començar a generar una càrrega elevada a MySQL segons les dades d'allotjament. Cada dia aquest valor superava el 120% de la norma per compte
  7. Segons Yandex. El lloc de Metrica és visitat per 100-200 persones al dia

En primer lloc, es va fer això:

  1. Les taules de bases de dades es van netejar de les escombraries acumulades
  2. Els connectors innecessaris es van desactivar, es van eliminar seccions de codi obsolet

Al mateix temps, m'agradaria cridar la vostra atenció sobre el fet que es van provar les opcions d'emmagatzematge en memòria cau (connectors de memòria cau), es van fer observacions, però la càrrega del 120% d'un lloc no va canviar i només va poder créixer.

Com era la càrrega aproximada de les bases de dades d'allotjament

Identifiquem potencials robots "malvats" i els bloquegem per IP
A la part superior hi ha el lloc en qüestió, just a sota hi ha altres llocs que tenen els mateixos cms i aproximadament el mateix trànsit, però creen menys càrrega.

Anàlisi

  • Es van fer molts intents amb opcions d'emmagatzematge en memòria cau de dades, les observacions es van dur a terme durant diverses setmanes (afortunadament, durant aquest temps, l'allotjament mai em va escriure que estava tan dolent i que estaria desconnectat)
  • Es va fer una anàlisi i cerca de consultes lentes, després es va canviar lleugerament l'estructura de la base de dades i el tipus de taula
  • Per a l'anàlisi, vam utilitzar principalment els AWStats integrats (per cert, va ajudar a calcular la pitjor adreça IP en funció del volum de trànsit
  • Mètrica: la mètrica només proporciona informació sobre persones, no sobre robots
  • Hi ha hagut intents d'utilitzar complements per a WP que poden filtrar i bloquejar els visitants fins i tot per país de ubicació i diverses combinacions.
  • Una manera completament radical va resultar ser tancar el lloc durant un dia amb la nota "Estem en manteniment"; això també es va fer amb el famós connector. En aquest cas, esperem que la càrrega baixi, però no a valors zero, ja que la ideologia WP es basa en ganxos i els connectors comencen la seva activitat quan es produeix un "ganxo", i abans que es produeixi el "ganxo", les sol·licituds a la base de dades poden ja s'ha fet

Idea

  1. Calcula les adreces IP que fan moltes sol·licituds en un curt període de temps.
  2. Anoteu el nombre de visites al lloc
  3. Bloqueja l'accés al lloc en funció del nombre de visites
  4. Bloqueja utilitzant l'entrada "Denega de" al fitxer .htaccess
  5. No vaig considerar altres opcions, com iptables i regles per a Nginx, perquè estic escrivint sobre l'allotjament

Ha aparegut una idea, així que cal implementar-la, ja que sense això...

  • Creació de taules per acumular dades
    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;
    
  • Creem un fitxer on col·locarem el codi. El codi es registrarà a les taules de candidats de bloqueig i conservarà un historial per a la depuració.

    Codi de fitxer per gravar adreces 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'essència del codi és obtenir l'adreça IP del visitant i escriure-la en una taula. Si la ip ja està a la taula, el camp cnt s'incrementarà (el nombre de peticions al lloc)

  • Ara el que fa por... Ara em cremaran per les meves accions :)
    Per gravar cada sol·licitud al lloc, connectem el codi del fitxer al fitxer principal de WordPress: wp-load.php. Sí, canviem el fitxer del nucli i precisament després que la variable global $wpdb ja existeix

Així doncs, ara podem veure amb quina freqüència es marca aquesta o aquella adreça IP a la nostra taula i amb una tassa de cafè hi mirem un cop cada 5 minuts per entendre la imatge.

Identifiquem potencials robots "malvats" i els bloquegem per IP

A continuació, simplement copieu la IP "perjudicial", obriu el fitxer .htaccess i afegiu-lo al final del fitxer.

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

Això és tot, ara 94.242.55.248 - no té accés al lloc i no genera càrrega a la base de dades

Però cada cop copiar a mà com aquesta no és una tasca molt justa i, a més, el codi pretenia ser autònom

Afegim un fitxer que s'executarà mitjançant CRON cada 30 minuts:

El codi del fitxer 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)));

El codi del fitxer és bastant senzill i primitiu i la seva idea principal és agafar candidats per bloquejar i introduir regles de bloqueig al fitxer .htaccess entre els comentaris.
# start_auto_deny_list i # end_auto_deny_list

Ara les IP "perjudicials" estan bloquejades per si mateixes i el fitxer .htaccess té un aspecte semblant a això:

# 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

Com a resultat, després que aquest codi comenci a funcionar, podeu veure el resultat al tauler d'allotjament:

Identifiquem potencials robots "malvats" i els bloquegem per IP

PD: El material és de l'autor, tot i que n'he publicat part a la meva web, n'he obtingut una versió més ampliada a Habre.

Font: www.habr.com

Afegeix comentari