Vi identificerer potentielle "onde" bots og blokerer dem via IP

Vi identificerer potentielle "onde" bots og blokerer dem via IP

God dag! I artiklen vil jeg fortælle dig, hvordan brugere af almindelig hosting kan fange IP-adresser, der genererer overdreven belastning på webstedet og derefter blokere dem ved hjælp af hostingværktøjer, der vil være "en lille smule" php-kode, et par skærmbilleder.

Input data:

  1. Hjemmeside oprettet på CMS WordPress
  2. Hosting Beget (dette er ikke en reklame, men admin panelets skærmbilleder vil være fra denne særlige hostingudbyder)
  3. WordPress-siden blev lanceret et sted i begyndelsen af ​​2000 og har et stort antal artikler og materialer
  4. PHP version 7.2
  5. WP har den nyeste version
  6. I nogen tid begyndte siden at generere en høj belastning på MySQL i henhold til hostingdataene. Hver dag oversteg denne værdi 120% af normen pr. konto
  7. Ifølge Yandex. Metrica-webstedet besøges af 100-200 mennesker om dagen

Først og fremmest blev dette gjort:

  1. Databasetabeller blev ryddet for akkumuleret affald
  2. Unødvendige plugins blev deaktiveret, dele af forældet kode blev fjernet

Samtidig vil jeg gerne henlede din opmærksomhed på, at caching muligheder (caching plugins) blev forsøgt, observationer blev foretaget - men belastningen på 120% fra én side var uændret og kunne kun vokse.

Hvordan den omtrentlige belastning på hostingdatabaser så ud

Vi identificerer potentielle "onde" bots og blokerer dem via IP
Øverst er det pågældende site, lige under er andre sider, der har samme cms og omtrent samme trafik, men skaber mindre belastning.

Analyse

  • Der blev gjort mange forsøg med muligheder for datacache, observationer blev udført over flere uger (heldigvis skrev hostingen mig aldrig i løbet af denne tid, at jeg var så dårlig og ville blive afbrudt)
  • Der var en analyse og søgning efter langsomme forespørgsler, derefter blev databasestrukturen og tabeltypen ændret lidt
  • Til analyse brugte vi primært de indbyggede AWStats (det hjalp i øvrigt med at beregne den dårligste IP-adresse baseret på trafikmængden
  • Metrisk - metrikken giver kun information om personer, ikke om bots
  • Der har været forsøg på at bruge plugins til WP, der kan filtrere og blokere besøgende selv efter land og forskellige kombinationer
  • En helt radikal måde viste sig at være at lukke siden i en dag med noten "Vi er under vedligeholdelse" - dette blev også gjort ved hjælp af det berømte plugin. I dette tilfælde forventer vi, at belastningen falder, men ikke til nul værdier, da WP-ideologien er baseret på hooks og plugins begynder deres aktivitet, når en "hook" opstår, og før "hook" opstår, kan anmodninger til databasen allerede være lavet

Idea

  1. Beregn IP-adresser, der laver mange anmodninger på kort tid.
  2. Registrer antallet af hits til webstedet
  3. Bloker adgangen til webstedet baseret på antallet af hits
  4. Bloker ved at bruge "Nægt fra"-posten i .htaccess-filen
  5. Jeg overvejede ikke andre muligheder, såsom iptables og regler for Nginx, fordi jeg skriver om hosting

Der er dukket en idé op, så den skal implementeres, da uden denne...

  • Oprettelse af tabeller til at akkumulere data
    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;
    
  • Lad os oprette en fil, hvori vi placerer koden. Koden vil registrere i de blokerende kandidattabeller og opbevare en historik for fejlretning.

    Filkode til registrering af IP-adresser

    <?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);
    
    

    Essensen af ​​koden er at få den besøgendes IP-adresse og skrive den ind i en tabel. Hvis ip'en allerede er i tabellen, vil cnt-feltet blive øget (antallet af anmodninger til webstedet)

  • Nu det skræmmende... Nu vil de brænde mig for mine handlinger :)
    For at registrere hver anmodning til webstedet forbinder vi filkoden til WordPress-hovedfilen - wp-load.php. Ja, vi ændrer kernefilen og præcis efter at den globale variabel $wpdb allerede eksisterer

Så nu kan vi se, hvor ofte denne eller hin IP-adresse er markeret i vores tabel, og med et krus kaffe kigger vi der en gang hvert 5. minut for at forstå billedet

Vi identificerer potentielle "onde" bots og blokerer dem via IP

Kopier derefter blot den "skadelige" IP, åbn .htaccess-filen og tilføj den til slutningen af ​​filen

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

Det er det, nu 94.242.55.248 - har ikke adgang til webstedet og genererer ikke belastning på databasen

Men hver gang at kopiere i hånden som denne er ikke en særlig retfærdig opgave, og desuden var koden beregnet til at være autonom

Lad os tilføje en fil, der vil blive udført via CRON hvert 30. minut:

Filkode, der ændrer .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)));

Filkoden er ret enkel og primitiv, og dens hovedidé er at tage kandidater til blokering og indtaste blokeringsregler i .htaccess-filen mellem kommentarerne
# start_auto_deny_list og # end_auto_deny_list

Nu er "skadelige" IP'er blokeret af sig selv, og .htaccess-filen ser sådan ud:

# 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

Som et resultat, efter at denne kode begynder at virke, kan du se resultatet i hostingpanelet:

Vi identificerer potentielle "onde" bots og blokerer dem via IP

PS: Materialet er forfatterens, selvom jeg udgav en del af det på min hjemmeside, fik jeg en mere udvidet version på Habre.

Kilde: www.habr.com

Tilføj en kommentar