Identificiramo potencialne "zlobne" bote in jih blokiramo po IP-ju

Identificiramo potencialne "zlobne" bote in jih blokiramo po IP-ju

Dober dan! V članku vam bom povedal, kako lahko uporabniki običajnega gostovanja ujamejo naslove IP, ki ustvarjajo prekomerno obremenitev spletnega mesta in jih nato blokirajo z orodji za gostovanje, "malo" bo php kode, nekaj posnetkov zaslona.

Vhodni podatki:

  1. Spletna stran izdelana na CMS WordPress
  2. Hosting Beget (to ni oglas, vendar bodo posnetki zaslona skrbniške plošče od tega posebnega ponudnika gostovanja)
  3. Stran WordPress je bila predstavljena nekje v začetku leta 2000 in ima veliko število člankov in materialov
  4. PHP različica 7.2
  5. WP ima najnovejšo različico
  6. Že nekaj časa je spletno mesto začelo ustvarjati veliko obremenitev MySQL glede na podatke gostovanja. Vsak dan je ta vrednost presegla 120 % norme na račun
  7. Glede na Yandex. Stran Metrica dnevno obišče 100-200 ljudi

Najprej je bilo to storjeno:

  1. Tabele baze podatkov so bile očiščene nabranega smeti
  2. Nepotrebni vtičniki so bili onemogočeni, deli zastarele kode so bili odstranjeni

Hkrati bi vas rad opozoril na dejstvo, da so bile preizkušene možnosti predpomnjenja (vtičniki za predpomnjenje), izvedena so bila opažanja - vendar je bila obremenitev 120% z enega mesta nespremenjena in je lahko samo rasla.

Kakšna je bila približna obremenitev gostiteljskih baz podatkov

Identificiramo potencialne "zlobne" bote in jih blokiramo po IP-ju
Na vrhu je obravnavana stran, čisto spodaj so druge strani, ki imajo enak cms in približno enak promet, vendar ustvarjajo manjšo obremenitev.

Analiza

  • Izvedenih je bilo veliko poskusov z možnostmi predpomnjenja podatkov, opazovanja so bila opravljena več tednov (na srečo mi v tem času gostovanje nikoli ni napisalo, da sem tako slab in da bo povezava prekinjena)
  • Opravljena je bila analiza in iskanje počasnih poizvedb, nato pa je bila nekoliko spremenjena struktura baze podatkov in vrsta tabele
  • Za analizo smo uporabili predvsem vgrajeni AWStats (mimogrede, pomagal je izračunati najslabši naslov IP glede na obseg prometa
  • Metrika - metrika zagotavlja informacije samo o ljudeh, ne o botih
  • Obstajajo poskusi uporabe vtičnikov za WP, ki lahko filtrirajo in blokirajo obiskovalce tudi glede na državo lokacije in različne kombinacije
  • Popolnoma radikalen način se je izkazal za zaprtje spletnega mesta za en dan z opombo "Smo v vzdrževanju" - to je bilo storjeno tudi s slavnim vtičnikom. V tem primeru pričakujemo, da bo obremenitev padla, vendar ne na ničelne vrednosti, saj ideologija WP temelji na kavljih in vtičniki začnejo svojo dejavnost, ko pride do »kavlja«, in preden se pojavi »kavelj«, lahko zahteve v bazo podatkov že narejeno

Ideja

  1. Izračunajte naslove IP, ki naredijo veliko zahtev v kratkem času.
  2. Zabeležite število obiskov spletnega mesta
  3. Blokirajte dostop do spletnega mesta glede na število zadetkov
  4. Blokirajte z vnosom »Zavrni od« v datoteki .htaccess
  5. Druge možnosti, kot so iptables in pravila za Nginx, nisem upošteval, ker pišem o gostovanju

Pojavila se je ideja, zato jo je treba uresničiti, saj brez tega...

  • Ustvarjanje tabel za zbiranje podatkov
    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;
    
  • Ustvarimo datoteko, v katero bomo umestili kodo. Koda se bo zapisala v tabele kandidatov za blokiranje in hranila zgodovino za odpravljanje napak.

    Koda datoteke za beleženje naslovov 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);
    
    

    Bistvo kode je pridobiti IP naslov obiskovalca in ga zapisati v tabelo. Če je ip že v tabeli, se bo polje cnt povečalo (število zahtev do spletnega mesta)

  • Zdaj pa strašljivo... Zdaj me bodo zažgali zaradi mojih dejanj :)
    Za beleženje vsake zahteve na spletno mesto povežemo kodo datoteke z glavno datoteko WordPress - wp-load.php. Da, spremenimo datoteko jedra in natančno potem, ko globalna spremenljivka $wpdb že obstaja

Zdaj lahko vidimo, kako pogosto je ta ali oni naslov IP označen v naši tabeli in z vrčkom kave vsakih 5 minut pogledamo tja, da razumemo sliko

Identificiramo potencialne "zlobne" bote in jih blokiramo po IP-ju

Nato preprosto kopirajte “škodljiv” IP, odprite datoteko .htaccess in jo dodajte na konec datoteke.

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

To je to, zdaj 94.242.55.248 - nima dostopa do spletnega mesta in ne ustvarja obremenitve baze podatkov

Toda vsakokratno ročno kopiranje, kot je to, ni zelo pravično opravilo, poleg tega pa naj bi bila koda avtonomna

Dodajmo datoteko, ki se bo izvajala prek CRON-a vsakih 30 minut:

Datotečna koda, ki spreminja .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)));

Koda datoteke je precej preprosta in primitivna, njena glavna ideja pa je vzeti kandidate za blokiranje in vnesti pravila blokiranja v datoteko .htaccess med komentarji
# start_auto_deny_list in # end_auto_deny_list

Zdaj so "škodljivi" IP-ji blokirani sami, datoteka .htaccess pa je videti nekako takole:

# 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

Posledično, potem ko ta koda začne delovati, lahko vidite rezultat na plošči gostovanja:

Identificiramo potencialne "zlobne" bote in jih blokiramo po IP-ju

PS: Gradivo je avtorsko, čeprav sem ga delno objavil na svoji spletni strani, na Habru sem dobil bolj razširjeno različico.

Vir: www.habr.com

Dodaj komentar