Մենք հայտնաբերում ենք պոտենցիալ «չար» բոտերին և արգելափակում դրանք IP-ի միջոցով

Մենք հայտնաբերում ենք պոտենցիալ «չար» բոտերին և արգելափակում դրանք IP-ի միջոցով

Լավ օր! Հոդվածում ես ձեզ կասեմ, թե ինչպես սովորական հոսթինգի օգտատերերը կարող են բռնել IP հասցեներ, որոնք կայքում ավելորդ բեռ են առաջացնում, այնուհետև արգելափակել դրանք հոսթինգի գործիքների միջոցով, կլինեն «մի քիչ» php կոդ, մի քանի սքրինշոթ:

Մուտքային տվյալներ.

  1. Կայքը ստեղծված է CMS WordPress-ով
  2. Հոսթինգ Beget (սա գովազդ չէ, բայց ադմինիստրատորի վահանակի սքրինշոթները կլինեն այս կոնկրետ հոստինգ մատակարարից)
  3. WordPress կայքը գործարկվել է ինչ-որ տեղ 2000 թվականի սկզբին և ունի մեծ թվով հոդվածներ և նյութեր
  4. PHP տարբերակը 7.2
  5. WP-ն ունի վերջին տարբերակը
  6. Արդեն որոշ ժամանակ է, ինչ կայքը սկսել է մեծ բեռնվածություն առաջացնել MySQL-ում՝ ըստ հոսթինգի տվյալների։ Ամեն օր այդ արժեքը գերազանցում էր մեկ հաշվի նորմայի 120%-ը
  7. Ըստ Yandex-ի. Metrica կայք օրական այցելում է 100-200 մարդ

Առաջին հերթին սա արվեց.

  1. Տվյալների բազայի աղյուսակները մաքրվել են կուտակված աղբից
  2. Ավելորդ փլագիններն անջատվեցին, հնացած կոդի բաժինները հեռացվեցին

Միևնույն ժամանակ, ես կցանկանայի ձեր ուշադրությունը հրավիրել այն փաստի վրա, որ փորձարկվել են քեշավորման տարբերակները (քեշավորման պլագինները), կատարվել են դիտարկումներ, բայց մեկ կայքից 120% բեռը անփոփոխ էր և կարող էր միայն աճել:

Ինչ տեսք ուներ հոսթինգի տվյալների բազաների մոտավոր ծանրաբեռնվածությունը

Մենք հայտնաբերում ենք պոտենցիալ «չար» բոտերին և արգելափակում դրանք IP-ի միջոցով
Վերևում խնդրո առարկա կայքն է, հենց ներքևում կան այլ կայքեր, որոնք ունեն նույն cms-ը և մոտավորապես նույն տրաֆիկը, բայց ավելի քիչ բեռ են ստեղծում:

Վերլուծություն

  • Բազմաթիվ փորձեր են արվել տվյալների քեշավորման տարբերակներով, դիտարկումներ են իրականացվել մի քանի շաբաթվա ընթացքում (բարեբախտաբար, այս ընթացքում հոսթինգն ինձ երբեք չի գրել, որ ես այդքան վատն եմ և կապակցվելու եմ)
  • Կատարվեց դանդաղ հարցումների վերլուծություն և որոնում, հետո մի փոքր փոխվեց տվյալների բազայի կառուցվածքը և աղյուսակի տեսակը
  • Վերլուծության համար մենք հիմնականում օգտագործեցինք ներկառուցված AWStats (ի դեպ, դա օգնեց հաշվարկել վատագույն IP հասցեն՝ ելնելով տրաֆիկի ծավալից
  • Մետրիկա - մետրիկը տեղեկատվություն է տրամադրում միայն մարդկանց, այլ ոչ բոտերի մասին
  • Փորձեր են եղել օգտագործել պլագիններ WP-ի համար, որոնք կարող են զտել և արգելափակել այցելուներին նույնիսկ ըստ գտնվելու վայրի երկրի և տարբեր համակցությունների:
  • Պարզվեց, որ ամբողջովին արմատական ​​միջոց է կայքը մեկ օրով փակել «Մենք տեխնիկական սպասարկման տակ ենք» նշումով, դա արվել է նաև հայտնի հավելվածի միջոցով: Այս դեպքում մենք ակնկալում ենք, որ բեռը կնվազի, բայց ոչ զրոյական արժեքների, քանի որ WP գաղափարախոսությունը հիմնված է կեռիկների վրա, և փլագինները սկսում են իրենց գործունեությունը, երբ տեղի է ունենում «կեռիկ», և մինչ «կեռիկը» տեղի է ունենում, տվյալների բազայի հարցումները կարող են: արդեն պատրաստված

Գաղափար

  1. Հաշվարկեք IP հասցեները, որոնք կարճ ժամանակահատվածում շատ հարցումներ են կատարում:
  2. Գրանցեք կայքի այցելությունների քանակը
  3. Արգելափակել մուտքը կայք՝ ելնելով այցելությունների քանակից
  4. Արգելափակել՝ օգտագործելով .htaccess ֆայլի «Մերժել» մուտքը
  5. Ես չեմ դիտարկել այլ տարբերակներ, ինչպիսիք են iptable-ները և Nginx-ի կանոնները, քանի որ գրում եմ հոսթինգի մասին

Գաղափար է առաջացել, ուստի այն պետք է իրականացնել, քանի որ առանց սրա...

  • Տվյալների կուտակման համար աղյուսակների ստեղծում
    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;
    
  • Եկեք ստեղծենք մի ֆայլ, որտեղ կտեղադրենք կոդը։ Կոդը կգրանցվի արգելափակող թեկնածուների աղյուսակներում և կպահի պատմություն վրիպազերծման համար:

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

    Կոդի էությունը այցելուի IP հասցեն ստանալն ու աղյուսակի մեջ գրելն է։ Եթե ​​ip-ն արդեն աղյուսակում է, ապա cnt դաշտը կավելանա (կայքի հարցումների քանակը)

  • Հիմա սարսափելին... Հիմա ինձ կվառեն արարքներիս համար :)
    Կայքում յուրաքանչյուր հարցում գրանցելու համար մենք ֆայլի կոդը միացնում ենք WordPress-ի հիմնական ֆայլին՝ wp-load.php: Այո, մենք փոխում ենք միջուկի ֆայլը և հենց այն բանից հետո, երբ $wpdb գլոբալ փոփոխականն արդեն գոյություն ունի

Այսպիսով, այժմ մենք կարող ենք տեսնել, թե որքան հաճախ է այս կամ այն ​​IP հասցեն նշվում մեր աղյուսակում և մի բաժակ սուրճով 5 րոպեն մեկ նայում ենք այնտեղ՝ պատկերը հասկանալու համար։

Մենք հայտնաբերում ենք պոտենցիալ «չար» բոտերին և արգելափակում դրանք IP-ի միջոցով

Այնուհետև պարզապես պատճենեք «վնասակար» IP-ն, բացեք .htaccess ֆայլը և ավելացրեք այն ֆայլի վերջում։

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

Վերջ, հիմա 94.242.55.248 - չունի մուտք դեպի կայք և բեռ չի առաջացնում տվյալների բազայի վրա

Բայց ամեն անգամ նման ձեռքով պատճենելը այնքան էլ արդար խնդիր չէ, և բացի այդ, ծածկագիրը նախատեսված էր ինքնավար լինելու համար

Եկեք ավելացնենք մի ֆայլ, որը կկատարվի CRON-ի միջոցով յուրաքանչյուր 30 րոպեն մեկ.

Ֆայլի կոդը փոփոխող .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)));

Ֆայլի կոդը բավականին պարզ է և պարզունակ, և դրա հիմնական գաղափարն է արգելափակման թեկնածուներ ընդունել և .htaccess ֆայլում արգելափակման կանոններ մուտքագրել մեկնաբանությունների միջև:
# start_auto_dery_list and # end_auto_urg_list

Այժմ «վնասակար» IP-ներն ինքնին արգելափակված են, և .htaccess ֆայլը ունի հետևյալ տեսքը.

# 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

Արդյունքում, այս կոդը սկսելուց հետո, դուք կարող եք տեսնել արդյունքը հոսթինգի վահանակում.

Մենք հայտնաբերում ենք պոտենցիալ «չար» բոտերին և արգելափակում դրանք IP-ի միջոցով

Հ.Գ.: Նյութը հեղինակինն է, թեև ես դրա մի մասը հրապարակել եմ իմ կայքում, բայց Habre-ում ստացել եմ ավելի ընդլայնված տարբերակը:

Source: www.habr.com

Добавить комментарий