Vi identifiserer potensielle "onde" roboter og blokkerer dem med IP

Vi identifiserer potensielle "onde" roboter og blokkerer dem med IP

God dag! I artikkelen vil jeg fortelle deg hvordan brukere av vanlig hosting kan fange IP-adresser som genererer overdreven belastning på nettstedet og deretter blokkere dem ved hjelp av hostingverktøy, det vil være "litt" php-kode, noen få skjermbilder.

Inndata:

  1. Nettsted laget på CMS WordPress
  2. Hosting Beget (dette er ikke en reklame, men skjermbildene fra adminpanelet vil være fra denne spesifikke vertsleverandøren)
  3. WordPress-siden ble lansert et sted tidlig i 2000 og har et stort antall artikler og materialer
  4. PHP versjon 7.2
  5. WP har den nyeste versjonen
  6. I en tid nå begynte siden å generere en høy belastning på MySQL i henhold til vertsdataene. Hver dag oversteg denne verdien 120 % av normen per konto
  7. I følge Yandex. Metrica-nettstedet besøkes av 100-200 personer per dag

Først og fremst ble dette gjort:

  1. Databasetabeller ble ryddet for akkumulert søppel
  2. Unødvendige plugins ble deaktivert, deler av utdatert kode ble fjernet

Samtidig vil jeg gjøre deg oppmerksom på det faktum at caching-alternativer (caching-plugins) ble prøvd, observasjoner ble gjort - men belastningen på 120% fra ett nettsted var uendret og kunne bare vokse.

Hvordan den omtrentlige belastningen på vertsdatabaser så ut

Vi identifiserer potensielle "onde" roboter og blokkerer dem med IP
Øverst er det aktuelle nettstedet, rett under er andre nettsteder som har samme cms og omtrent samme trafikk, men som skaper mindre belastning.

Analyse

  • Det ble gjort mange forsøk med alternativer for databufring, observasjoner ble utført over flere uker (heldigvis, i løpet av denne tiden skrev vertskapet aldri til meg at jeg var så dårlig og ville bli frakoblet)
  • Det ble en analyse og søk etter trege søk, deretter ble databasestrukturen og tabelltypen endret litt
  • For analyse brukte vi først og fremst de innebygde AWStats (det hjalp forresten å beregne den dårligste IP-adressen basert på trafikkvolum
  • Metrikk – beregningen gir kun informasjon om personer, ikke om roboter
  • Det har vært forsøk på å bruke plugins for WP som kan filtrere og blokkere besøkende selv etter land og forskjellige kombinasjoner
  • En helt radikal måte viste seg å være å stenge nettstedet for en dag med meldingen "Vi er under vedlikehold" - dette ble også gjort ved hjelp av den berømte plugin. I dette tilfellet forventer vi at belastningen faller, men ikke til null verdier, siden WP-ideologien er basert på kroker og plugins begynner sin aktivitet når en "hook" oppstår, og før "hook" oppstår, kan forespørsler til databasen allerede være laget

Idé

  1. Beregn IP-adresser som gjør mange forespørsler på kort tid.
  2. Registrer antall treff på nettstedet
  3. Blokker tilgang til nettstedet basert på antall treff
  4. Blokkér ved å bruke «Nekt fra»-oppføringen i .htaccess-filen
  5. Jeg vurderte ikke andre alternativer, som iptables og regler for Nginx, fordi jeg skriver om hosting

En idé har dukket opp, så den må implementeres, siden uten denne...

  • Lage tabeller for å samle 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;
    
  • La oss lage en fil der vi skal plassere koden. Koden vil registrere i de blokkerende kandidattabellene og holde en historikk for feilsøking.

    Filkode for registrering av 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 av koden er å få den besøkendes IP-adresse og skrive den inn i en tabell. Hvis ip-en allerede er i tabellen, vil cnt-feltet økes (antall forespørsler til nettstedet)

  • Nå er det skumle... Nå skal de brenne meg for mine handlinger :)
    For å registrere hver forespørsel til nettstedet, kobler vi filkoden til hoved WordPress-filen - wp-load.php. Ja, vi endrer kjernefilen og nøyaktig etter at den globale variabelen $wpdb allerede eksisterer

Så nå kan vi se hvor ofte denne eller den IP-adressen er merket i tabellen vår, og med et kopp kaffe ser vi dit en gang hvert 5. minutt for å forstå bildet

Vi identifiserer potensielle "onde" roboter og blokkerer dem med IP

Deretter kopierer du bare den "skadelige" IP-en, åpner .htaccess-filen og legger den til på slutten av filen

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

Det er det, nå 94.242.55.248 - har ikke tilgang til nettstedet og genererer ikke belastning på databasen

Men hver gang å kopiere for hånd som dette er ikke en veldig rettferdig oppgave, og dessuten var koden ment å være autonom

La oss legge til en fil som vil bli utført via CRON hvert 30. minutt:

Filkode som endrer .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 ganske enkel og primitiv, og hovedideen er å ta kandidater for blokkering og angi blokkeringsregler i .htaccess-filen mellom kommentarene
# start_auto_deny_list og # end_auto_deny_list

Nå er "skadelige" IP-er blokkert av seg selv, og .htaccess-filen ser omtrent slik ut:

# 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, etter at denne koden begynner å fungere, kan du se resultatet i vertspanelet:

Vi identifiserer potensielle "onde" roboter og blokkerer dem med IP

PS: Materialet er forfatterens, selv om jeg publiserte deler av det på nettsiden min, fikk jeg en mer utvidet versjon på Habre.

Kilde: www.habr.com

Legg til en kommentar