Mēs identificējam potenciālos "ļaunos" robotus un bloķējam tos pēc IP

Mēs identificējam potenciālos "ļaunos" robotus un bloķējam tos pēc IP

Laba diena! Rakstā es pastāstīšu, kā parastā hostinga lietotāji var noķert IP adreses, kas rada pārmērīgu slodzi vietnē un pēc tam tās bloķēt, izmantojot hostinga rīkus, būs “mazliet” php koda, daži ekrānšāviņi.

Ievaddati:

  1. Vietne izveidota uz CMS WordPress
  2. Hostings Beget (šī nav reklāma, bet administratora paneļa ekrānuzņēmumi būs no šī konkrētā mitināšanas pakalpojumu sniedzēja)
  3. WordPress vietne tika palaista kaut kur 2000. gada sākumā, un tajā ir daudz rakstu un materiālu
  4. PHP versija 7.2
  5. WP ir jaunākā versija
  6. Jau kādu laiku vietne sāka radīt lielu MySQL slodzi saskaņā ar mitināšanas datiem. Katru dienu šī vērtība pārsniedza 120% no normas vienā kontā
  7. Saskaņā ar Yandex. Metrica vietni dienā apmeklē 100-200 cilvēku

Pirmkārt, tas tika darīts:

  1. Datu bāzes tabulas tika iztīrītas no uzkrātajiem atkritumiem
  2. Tika atspējoti nevajadzīgi spraudņi, noņemtas novecojušā koda sadaļas

Tajā pašā laikā vēlos vērst uzmanību uz to, ka tika izmēģinātas kešatmiņas iespējas (kešatmiņas spraudņi), veikti novērojumi - bet 120% slodze no vienas vietnes bija nemainīga un varēja tikai augt.

Kā izskatījās aptuvenā mitināšanas datu bāzu slodze

Mēs identificējam potenciālos "ļaunos" robotus un bloķējam tos pēc IP
Augšpusē ir attiecīgā vietne, tieši zemāk ir citas vietnes, kurām ir vienādi cm un aptuveni vienāda trafika, taču tās rada mazāku slodzi.

Analīze

  • Tika veikti daudzi mēģinājumi ar datu kešatmiņas iespējām, novērojumi tika veikti vairāku nedēļu garumā (par laimi šajā laikā hostings man nekad neatrakstīja, ka man ir tik slikti un tiks atvienots)
  • Notika lēno vaicājumu analīze un meklēšana, pēc tam tika nedaudz mainīta datu bāzes struktūra un tabulas veids
  • Analīzei mēs galvenokārt izmantojām iebūvētos AWStats (starp citu, tas palīdzēja aprēķināt sliktāko IP adresi, pamatojoties uz trafika apjomu
  • Metrika — metrika sniedz informāciju tikai par cilvēkiem, nevis par robotprogrammatūru
  • Ir bijuši mēģinājumi izmantot WP spraudņus, kas var filtrēt un bloķēt apmeklētājus pat pēc atrašanās vietas valsts un dažādām kombinācijām
  • Izrādījās pilnīgi radikāls veids, kā aizvērt vietni uz dienu ar piezīmi “Mēs veicam apkopi” - arī tas tika darīts, izmantojot slaveno spraudni. Šajā gadījumā mēs sagaidām, ka slodze samazināsies, bet ne līdz nulles vērtībām, jo ​​WP ideoloģija ir balstīta uz āķiem un spraudņi sāk savu darbību, kad notiek "āķis", un pirms "āķa" var tikt nosūtīti pieprasījumi datu bāzei. jau ir izgatavots

Ideja

  1. Aprēķiniet IP adreses, kas īsā laika periodā veic daudz pieprasījumu.
  2. Reģistrējiet vietnes trāpījumu skaitu
  3. Bloķējiet piekļuvi vietnei, pamatojoties uz trāpījumu skaitu
  4. Bloķējiet, izmantojot .htaccess faila ierakstu “Liegt no”.
  5. Es neapsvēru citas iespējas, piemēram, iptables un Nginx noteikumus, jo es rakstu par mitināšanu

Ir radusies ideja, tāpēc jāīsteno, kā bez šī...

  • Tabulu izveide datu uzkrāšanai
    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;
    
  • Izveidosim failu, kurā ievietosim kodu. Kods tiks ierakstīts bloķēšanas kandidātu tabulās un saglabās atkļūdošanas vēsturi.

    Faila kods IP adrešu ierakstīšanai

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

    Koda būtība ir iegūt apmeklētāja IP adresi un ierakstīt to tabulā. Ja ip jau ir tabulā, cnt lauks tiks palielināts (pieprasījumu skaits vietnei)

  • Tagad baigais... Tagad mani sadedzinās par manu rīcību :)
    Lai reģistrētu katru pieprasījumu vietnei, mēs savienojam faila kodu ar galveno WordPress failu - wp-load.php. Jā, mēs mainām kodola failu un tieši pēc tam, kad globālais mainīgais $wpdb jau pastāv

Tātad, tagad mēs varam redzēt, cik bieži mūsu tabulā ir atzīmēta šī vai cita IP adrese un ar kafijas krūzi mēs tur paskatāmies reizi 5 minūtēs, lai saprastu attēlu

Mēs identificējam potenciālos "ļaunos" robotus un bloķējam tos pēc IP

Pēc tam vienkārši nokopējiet “kaitīgo” IP, atveriet .htaccess failu un pievienojiet to faila beigās.

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

Tas ir viss, tagad 94.242.55.248 - nav piekļuves vietnei un tas nerada datubāzes slodzi

Bet katru reizi šādi kopēt ar roku nav ļoti taisnīgs uzdevums, turklāt kods bija paredzēts kā autonoms

Pievienosim failu, kas tiks izpildīts, izmantojot CRON ik pēc 30 minūtēm:

Faila kods, kas modificē .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)));

Faila kods ir diezgan vienkāršs un primitīvs, un tā galvenā ideja ir ņemt bloķēšanas kandidātus un ievadīt bloķēšanas noteikumus .htaccess failā starp komentāriem.
# start_auto_deny_list un # end_auto_deny_list

Tagad “kaitīgie” IP tiek bloķēti paši par sevi, un .htaccess fails izskatās apmēram šādi:

# 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

Rezultātā pēc tam, kad šis kods sāk darboties, jūs varat redzēt rezultātu hostinga panelī:

Mēs identificējam potenciālos "ļaunos" robotus un bloķējam tos pēc IP

PS: Materiāls ir autora, lai gan daļu no tā es publicēju savā tīmekļa vietnē, es ieguvu plašāku versiju par Habre.

Avots: www.habr.com

Pievieno komentāru