Identificamos potenciales bots “malvados” y los bloqueamos por IP

Identificamos potenciales bots “malvados” y los bloqueamos por IP

¡Buen día! En el artículo te contaré cómo los usuarios de hosting normal pueden capturar direcciones IP que generan una carga excesiva en el sitio y luego bloquearlas usando herramientas de hosting, habrá “un poquito” de código php, algunas capturas de pantalla.

Datos de entrada:

  1. Sitio web creado en CMS WordPress
  2. Hosting Beget (esto no es un anuncio, pero las capturas de pantalla del panel de administración serán de este proveedor de alojamiento en particular)
  3. El sitio de WordPress se lanzó a principios de 2000 y tiene una gran cantidad de artículos y materiales.
  4. PHP versión 7.2
  5. WP tiene la última versión
  6. Desde hace algún tiempo, el sitio comenzó a generar una gran carga en MySQL según los datos del alojamiento. Cada día este valor superó el 120% de la norma por cuenta.
  7. Según Yandex. El sitio de Metrica es visitado por 100-200 personas por día

En primer lugar se hizo esto:

  1. Se limpiaron las tablas de la base de datos de basura acumulada.
  2. Se desactivaron complementos innecesarios y se eliminaron secciones de código obsoleto.

Al mismo tiempo, me gustaría llamar su atención sobre el hecho de que se probaron opciones de almacenamiento en caché (complementos de almacenamiento en caché), se hicieron observaciones, pero la carga del 120% de un sitio no cambió y solo pudo crecer.

Cómo era la carga aproximada en las bases de datos de alojamiento

Identificamos potenciales bots “malvados” y los bloqueamos por IP
En la parte superior está el sitio en cuestión, justo debajo hay otros sitios que tienen los mismos cms y aproximadamente el mismo tráfico, pero generan menos carga.

análisis de

  • Se hicieron muchos intentos con las opciones de almacenamiento en caché de datos, las observaciones se llevaron a cabo durante varias semanas (afortunadamente, durante este tiempo el hosting nunca me escribió que estaba tan mal y que me desconectarían)
  • Hubo un análisis y búsqueda de consultas lentas, luego se cambiaron ligeramente la estructura de la base de datos y el tipo de tabla.
  • Para el análisis, utilizamos principalmente AWStats integrado (por cierto, nos ayudó a calcular la peor dirección IP en función del volumen de tráfico).
  • Métrica: la métrica proporciona información solo sobre personas, no sobre bots.
  • Ha habido intentos de utilizar complementos para WP que pueden filtrar y bloquear visitantes incluso por país de ubicación y varias combinaciones.
  • Una forma completamente radical resultó ser cerrar el sitio por un día con la nota "Estamos en mantenimiento"; esto también se hizo usando el famoso complemento. En este caso, esperamos que la carga disminuya, pero no a valores cero, ya que la ideología de WP se basa en ganchos y los complementos comienzan su actividad cuando ocurre un "gancho", y antes de que ocurra el "gancho", las solicitudes a la base de datos pueden ya esta hecho

Idea

  1. Calcule direcciones IP que realizan muchas solicitudes en un corto período de tiempo.
  2. Registre el número de visitas al sitio.
  3. Bloquear el acceso al sitio según el número de visitas
  4. Bloquear usando la entrada "Denegar desde" en el archivo .htaccess
  5. No consideré otras opciones, como iptables y reglas para Nginx, porque estoy escribiendo sobre hosting.

Ha aparecido una idea, por lo que hay que implementarla, ya que sin ella...

  • Crear tablas para acumular datos.
    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;
    
  • Creemos un archivo en el que colocaremos el código. El código se registrará en las tablas de candidatos de bloqueo y mantendrá un historial para la depuración.

    Código de archivo para registrar direcciones 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);
    
    

    La esencia del código es obtener la dirección IP del visitante y escribirla en una tabla. Si la ip ya está en la tabla, se aumentará el campo cnt (el número de solicitudes al sitio)

  • Ahora lo que da miedo... Ahora me quemarán por mis acciones :)
    Para registrar cada solicitud al sitio, conectamos el código del archivo al archivo principal de WordPress: wp-load.php. Si, cambiamos el archivo del kernel y precisamente después ya existe la variable global $wpdb

Entonces, ahora podemos ver con qué frecuencia se marca esta o aquella dirección IP en nuestra tabla y con una taza de café miramos allí una vez cada 5 minutos para entender la imagen.

Identificamos potenciales bots “malvados” y los bloqueamos por IP

Luego simplemente copie la IP “dañina”, abra el archivo .htaccess y agréguelo al final del archivo.

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

Eso es todo, ahora 94.242.55.248: no tiene acceso al sitio y no genera carga en la base de datos.

Pero siempre copiar a mano de esta manera no es una tarea muy justa y, además, el código estaba destinado a ser autónomo.

Agreguemos un archivo que se ejecutará vía CRON cada 30 minutos:

Código de archivo modificando .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 código del archivo es bastante simple y primitivo y su idea principal es tomar candidatos para bloquear e ingresar reglas de bloqueo en el archivo .htaccess entre los comentarios.
# start_auto_deny_list y # end_auto_deny_list

Ahora las IP "dañinas" se bloquean solas y el archivo .htaccess se parece a esto:

# 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

Como resultado, después de que este código comience a funcionar, podrá ver el resultado en el panel de alojamiento:

Identificamos potenciales bots “malvados” y los bloqueamos por IP

PD: El material es del autor, aunque publiqué parte en mi sitio web, conseguí una versión más ampliada en Habre.

Fuente: habr.com

Añadir un comentario