
Mirë dita! Në këtë artikull do të tregoj si përdoruesit e hostimit të zakonshëm mund të kapin adresat IP që krijojnë një ngarkesë të tepruar në sit dhe më pas të bllokojnë ato me ndihmën e mjeteve të hostimit, do të ketë disa rreshta të kodit PHP, disa screenshot-e.
Të dhënat hyrëse:
- Siti i krijuar në CMS WordPress
- Hostim Beget (kjo nuk është reklamë, por screenshot-et e panelit do të jenë të këtij ofruesi hostimi)
- Siti WordPress është aktivizuar diku në fillim të viteve 2000 dhe ka një numër të madh artikujsh dhe materialesh
- Versioni PHP 7.2
- WP ka versionin e fundit
- Që nga disa kohë, siti ka filluar të gjenerojë një ngarkesë të lartë në MySQL sipas të dhënave të hostimit. Çdo ditë ky vlerë tejkalonte 120% të normës për llogarinë
- Sipas Yandex.Metrica, siti vizitohet nga 100-200 njerëz në ditë
Në radhë të parë është bërë:
- Tabela e DB janë pastruar nga mbeturinat e akumuluara
- Janë çaktivizuar plugin-at e panevojshme, janë hequr pjesë të kodit të vjetruar
Këtu po e theksoj, janë provuar variante të keshimit (plugin-at e keshimit), janë kryer vëzhgime – por ngarka 120% nga një sit mbeti e pandryshuar dhe mund të rritej vetëm.
Kështu dukeshin ngarkesat në bazën e të dhënave të hostimit

Në krye ndodhet siti për të cilin bëhet fjalë, pak më poshtë janë të tjerë site që kanë të njëjtin CMS dhe një frekuencë të ngjashme vizitash, por krijojnë më pak ngarkesë.
Analiza
- Janë bërë shumë përpjekje me variante të keshimit të të dhënave, janë realizuar vëzhgime për disa javë (fatmirësisht, hostimi gjatë kësaj kohe nuk më dërgoi asnjë herë mesazh se isha kaq i keq dhe do të më çaktivizonte)
- Ka qenë analiza dhe kërkimi i kërkesave të ngadalta, më pas është ndryshuar pak struktura e DB dhe tipi i tabelave
- Për analizë fillimisht është përdorur AWStats i integruar (ai ndihmoi për të gjetur adresën IP më të dëmshme sipas volumit të trafikut)
- Metrika - metrika jep vetëm informacion për njerëzit, jo për botet
- Janë bërë përpjekje për të përdorur plugin-a për WP, që dinë të filtrojnë dhe bllokojnë vizitorët madje sipas vendndodhjes dhe kombinimeve të ndryshme
- Një mënyrë krejt radikale ishte të mbyllej siti për një ditë me shënimin "Ne jemi në mirëmbajtje" - kjo gjithashtu u realizua me ndihmën e plugin-it të njohur. Në këtë rast, ngarkesa pritej të binte, por jo në 0, pasi ideologjia e WP bazohet në hooks dhe plugin-at nisin aktivitetin e tyre kur ndodhin ndonjë "hook", dhe para se të ndodhin "hooks" mund të ishin bërë tashmë kërkesa në DB
Ideja
- Të llogarisi adresat IP që bëjnë shumë kërkesa në një periudhë të shkurtër
- Të regjistrojë numrin e kërkesave në sit
- Bllokimi i aksesit në sit në bazë të numrit të kërkesave
- Bllokimi me anë të regjistrimit "Deny from" në skedarin .htaccess
- Mënyra të tjera, si iptables dhe rregullat për Nginx, nuk shqyrtohen, pasi po shkruaj për hostimin
Ergën idenë, siç është e domosdoshme ta zbatojmë, si pa këtë…
- Krijojmë tabela për akumulimin e të dhënave
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='Kandidatët për bllokim' 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='Lista e bllokuar tashmë' 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='Historia e të gjitha kërkesave për debugging' COLLATE='utf8_general_ci' ENGINE=InnoDB AUTO_INCREMENT=1; - Të krijojmë një skedë ku do të vendosim kodin. Kodi do të regjistrojë në tabelat e kandidatëve për bllokim dhe do të mbajë historikun për debugging.
Kodi i skedës, për regjistrimin e adresave 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);Qëllimi i kodit është të marrë adresën IP të vizitorit dhe ta regjistrojë atë në tabelë. Nëse ip është tashmë në tabelë, do të rritet fushë cnt (numri i kërkesave në site)
- Tani ajo që është e frikshme… Tani do të më këndojnë për veprimet e mia 🙂
Për të regjistruar çdo kërkesë në sit, lidhim kodin e skedës me skedarin kryesor të WordPress - wp-load.php. Po, në të vërtetë po e ndryshojmë skedarin e bërthamës, dhe pikërisht pasi ekziston variabla globale $wpdb
Tani, mund të shohim se sa shpesh një adresë IP e caktuar shfaqet në tabelën tonë dhe me një filxhan kafe shkojmë aty çdo 5 minuta për të kuptuar situatën

Më pas është e thjeshtë, kopjova IP-në "e dëmshme", hapëm skedarin .htaccess dhe e shtuam në fund të skedarit
Order allow,deny
Allow from all
# start_auto_deny_list
Deny from 94.242.55.248
# end_auto_deny_list
Tani, 94.242.55.248 - nuk ka qasje në sit dhe nuk gjeneron ngarkesë në DB
Porë se çdo herë nuk është shumë e drejtë të kopjosh me duar, dhe po ashtu, kodi është menduar si autonom.
Do të shtojmë një skedë që do të ekzekutohet nëpërmjet CRON çdo 30 minuta:
Kodi i skedës që modifikon .htaccess
get_results("SELECT * FROM wp_visiters_bot WHERE cnt>{$limit_cnt}");
$new_blocked = [];
$exclude_ip = [
'87.236.16.70' // adresa e hostimit
];
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;
}
// Pastrimi i tabelës
$wpdb->query("DELETE FROM wp_visiters_bot");
//echo '';print_r($new_blocked);echo '
';$file = '.htaccess';$start_searche_tag = 'start_auto_deny_list';$end_searche_tag = 'end_auto_deny_list';$handle = @fopen($file, "r"); if ($handle) {$replace_string = ''; // Test për të futur në skedën .htaccess$target_content = false; // Flamuri për pjesën e nevojshme të koditwhile (($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 "Gabim: fgets() humbi papritur"; } fclose($handle); }// Skeda aktuale .htaccess $content = file_get_contents($file);$content = str_replace($replace_string, '', $content);// Pastrimi i të gjitha bllokimeve në skedën .htaccess file_put_contents($file, $content);// Shkrimi i bllokimeve të reja $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))); Kodi i skedës është mjaft i thjeshtë dhe primitiv, dhe ideja kryesore e tij është të marrë kandidatët për bllokim dhe t'i shtojë rregullat e bllokimit në skedën .htaccess ndërmjet komenteve.
# start_auto_deny_list и # end_auto_deny_list
Tani, adresat IP "të këqija" bllokohen vetë, dhe skeda .htaccess duket përafërsisht kështu:
# 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
Si rezultat, pas fillimit të veprimit të këtij kodi, mund të shihni rezultatin në panelin e hostimit:

PS: Materiali është autorësor, edhe pse një pjesë e tij e kam botuar në faqen time, por në Habre doli një version më të zgjeruar.
Burimi: habr.com
