Kiszűrjük a potenciális "rossz" botokat és blokkoljuk őket IP cím alapján

Kiszűrjük a potenciális "rossz" botokat és blokkoljuk őket IP cím alapján

Jó napot! A cikkben elmondom, hogyan lehet a legegyszerűbb tárhelyhasználók IP-címét nyomon követni, amelyek túlzott terhelést generálnak a weboldalon, majd blokkolni őket a tárhelyszolgáltató eszközeivel. Lesz egy kis php kód, néhány képernyőkép.

Beviteli adatok:

  1. A weboldal CMS WordPress alapú
  2. Tárhely Beget (ez nem reklám, de az adminisztrációs képernyőképek ennek a szolgáltatónak az eredményei lesznek)
  3. A WordPress weboldal valamikor a 2000-es évek elején indult, és nagy mennyiségű cikket és anyagot tartalmaz
  4. PHP verzió 7.2
  5. A WP legújabb verziója van
  6. Egy ideje a weboldal magas terhelést generál a MySQL-en a tárhelyszolgáltató adatai szerint. Naponta ez az érték meghaladta a 120%-át a normának a fiókhoz képest
  7. A Yandex.Metrika szerint a weboldalt napi 100-200 ember látogatja

Elsősorban az alábbiakat tettük:

  1. Tisztítottuk az adatbázis tábláit a felhalmozódott szeméttől
  2. Kikapcsoltuk a felesleges bővítményeket, eltávolítottuk a régi kódokat

Figyelembe kell venni, hogy kipróbáltuk a cache-elési lehetőségeket (cache bővítmények), és megfigyeléseket végeztünk - de a 120% terhelés egy weboldalról változatlan maradt és csak nőhetett.

Íme, hogyan nézett ki a példaként a tárhelyszolgáltató adatbázisának terhelése

Kiszűrjük a potenciális "rossz" botokat és blokkoljuk őket IP cím alapján
A listán a szóban forgó weboldal van az élen, kicsit lejjebb más weboldalak, amelyek ugyanazon CMS-t használják és hasonló látogatottságot mutatnak, de kevesebb terhelést generálnak.

Elemzés

  • Sok kísérletet tettünk az adatok cache-elésére, megfigyeléseket végeztünk néhány héten át (szerencsére a tárhelyszolgáltató az idő alatt soha nem írt nekem, hogy milyen rossz vagyok, és kikapcsolnak)
  • Elemzés és lassú lekérdezések keresése folyt, majd kissé megváltozott az adatbázis struktúrája és a táblák típusa
  • Elemzéshez elsősorban az alapértelmezett AWStats-t használták (ami segített meghatározni a legrosszabb IP-címet a forgalom mennyisége alapján)
  • A Metrika csak az emberekről ad tájékoztatást, nem pedig a botokról
  • Kísérletek történtek olyan WP bővítmények használatára, amelyek képesek szűrni és blokkolni a látogatókat az országuk alapján, valamint különféle kombinációk szerint
  • A completely radical way to take down the site for a day with a note "We are undergoing maintenance" was done using the famous plugin. The load is expected to have dropped, but not to zero, as the ideology of WP is based on hooks, and plugins become active upon the occurrence of any "hook," and database requests may have already been made before the "hook" occurs.

Ötlet

  1. Calculate the IP addresses that make many requests in a short period.
  2. Record the number of visits to the site.
  3. Based on the number of visits, block access to the site.
  4. Block using the "Deny from" entry in the .htaccess file.
  5. Other options, like iptables and Nginx rules, are not considered since I'm writing about hosting.

An idea has appeared, so it must be implemented; how can we do without it...

  • We create tables to accumulate 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='Candidates for blocking'
    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='List of already blocked'
    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='History of all requests for debugging'
    COLLATE='utf8_general_ci'
    ENGINE=InnoDB
    AUTO_INCREMENT=1;
    
  • Let's create a file where we will place the code. The code will record entries in the candidate tables for blocking and maintain a history for debugging.

    File code for recording IP addresses.

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

    The essence of the code is to get the visitor's IP address and record it in the table. If the IP already exists in the table, the field cnt (number of requests to the site) will be incremented.

  • Now for the scary part… I might get burned for my actions 🙂
    To record each visit to the site, we include the file code in the main WordPress file — wp-load.php. Yes, we are exactly modifying the core file, and only after the global variable $wpdb already exists.

Most már láthatjuk, hogy melyik IP-címeket hányszor jelöltük meg a táblázatunkban, és egy csésze kávéval körülbelül 5 percenként belenézünk a helyzet áttekintéséhez.

Kiszűrjük a potenciális "rossz" botokat és blokkoljuk őket IP cím alapján

Ezután egyszerű, másoltuk a "káros" IP-t, megnyitottuk a .htaccess fájlt, és hozzáadtuk a fájl végéhez.

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

Kész, most már a 94.242.55.248-nak nincs hozzáférése az oldalhoz, és nem generál terhelést az adatbázisra.

De így kézzel lemásolni, nem éppen a legjobb elfoglaltság, ráadásul a kódot úgy tervezték, hogy autonóm legyen.

Adjunk hozzá egy fájlt, amely 30 percenként fut le a CRON-on:

A .htaccess fájlt módosító kód

get_results("SELECT * FROM wp_visiters_bot WHERE cnt>{$limit_cnt}");

$new_blocked = [];

$exclude_ip = [
    '87.236.16.70' //a hoszt IP címe
];

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;
}

//Tábla törlés
$wpdb->query("DELETE FROM wp_visiters_bot");

//$file = '.htaccess';

$start_searche_tag = 'start_auto_deny_list';

$end_searche_tag = 'end_auto_deny_list';

$handle = @fopen($file, "r");
if ($handle) {

    $replace_string = ''; //Teszt a .htaccess fájlba történő beillesztéshez

    $target_content = false; //A szükséges kódblokk jelzője

    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 "Hiba: fgets() váratlanul hibázott.\n";
    }
    fclose($handle);
}

//Jelenlegi .htaccess fájl
$content = file_get_contents($file);

$content = str_replace($replace_string, '', $content);

//Minden blokkolás tisztítása a .htaccess fájlban
file_put_contents($file, $content);

//Új blokkolások rögzítése
$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)));

A fájl kódja elég egyszerű és primitív, és a fő ötlete az, hogy a blokkolásra szánt jelölteket átveszi, és a blokkolási szabályokat beírja a .htaccess fájlba a megjegyzések között.
# start_auto_deny_list и # end_auto_deny_list

Most a "káros" IP-k blokkolják önmagukat, és a .htaccess fájl körülbelül így néz ki:

# 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

A kód aktiválása után a hosting vezérlőpulton láthatjuk az eredményt:

Kiszűrjük a potenciális "rossz" botokat és blokkoljuk őket IP cím alapján

PS: Az anyag szerzői jogvédelem alatt áll, bár egy része megjelent a saját weboldalamon, de a Habron egy kibővített verzió készült.

Forrás: habr.com

Megbízható hosting vásárlása weboldalak számára, DDoS védelemmel, VPS VDS szerverek. 🔥 Megbízható webtárhely vásárlása DDoS védelemmel, VPS VDS szerverek | ProHoster