
Tere päevast! Selles artiklis räägin, kuidas tavahostingu kasutajad saavad tuvastada IP-aadresse, mis genereerivad veebilehele liigset koormust, ja kuidas neid seejärel hostinguressursside abil blokeerida, sisaldades natukene PHP koodi ja mitmeid ekraanipilte.
Sisendandmed:
- Veebisait, mis on loodud CMS WordPressi alusel
- Veebihosting Beget (see ei ole reklaam, kuid adminpaneeli ekraanipildid tulevad just sellelt hostingu pakkujalt)
- WordPressi sait käivitati kuskil 2000. aastate alguses ja seal on palju artikleid ja materjale
- PHP versioon 7.2
- WP-l on uusim versioon
- Viimasel ajal on sait hakanud genereerima kõrget koormust MySQL-le vastavalt hostingu andmetele. Iga päev ületas see väärtus 120% normist kontole.
- Yandex.Metrica andmetel külastab saiti 100-200 inimest päevas
Esiteks tehti:
- Andmebaasi tabelid puhastati kogunenud prügist
- Käivitati mittevajalikud pluginate välja lülitamine ja vananenud koodi eemaldamine
Samuti juhin tähelepanu, et testiti vahemälu variante (vahemälu pluginaid), tehti jälgimisi — kuid koormus 120% ühelt saidilt jäi muutumatuks ja võis ainult kasvada.
Nii nägi välja ligikaudne koormus hostingu andmebaasides

Tippides on sait, millest räägitakse, veidi allpool on teised saidid, mis kasutavad sama CMS-i ja omavad ligikaudu sarnast külastust, kuid tekitavad vähem koormust.
Analüüs
- Oli palju katseid andmete vahemällu salvestamise variandid, jälgiti mitme nädala jooksul (õnneks ei ole hostimine selle aja jooksul kunagi öelnud, et olen nii halb ja mind katkestatakse).
- Tehti analüüse ja otsiti aeglaseid päringuid, seejärel muudeti veidi DB struktuuri ja tabelite tüüpe.
- Analüüsimiseks kasutati esmalt sisseehitatud AWStats'i (see aitas muide leida kõige pahatahtlikuma IP-aadressi liikluse mahult).
- Mõõdik — mõõdik annab teavet ainult inimeste kohta, mitte robotite kohta.
- Oli katseid kasutada WP pluginaid, mis suudavad filtreerida ja blokeerida külastajaid isegi asukoha ja erinevate kombinatsioonide põhjal.
- Radikaalne lahendus saidi sulgemiseks üheks päevaks sildiga „Me oleme hoolduses“ kasutades tuntud pistikprogrammi. Sel juhul oodata koormuse langust, kuid mitte nulli, kuna WP ideoloogia põhineb hookidel ja pistikprogrammid aktiviseeruvad mõne „hooki“ tekkimise korral, ning enne „hooki“ tekkimist võivad juba olla tehtud päringud andmebaasi.
Idee
- Arvuta IP-aadressid, mis teevad lühikese aja jooksul palju päringuid.
- Salvesta saidi külastuste arv.
- Põhinedes külastuste arvule, blokeeri juurdepääs saidile.
- Blokeeri „Deny from“ kirje abil failis .htaccess.
- Teised variandid nagu iptables ja Nginxi reeglid ei ole kaalumisel, kuna kirjutan hostimise kohta.
Kui idee on tekkinud, siis tuleb see ellu viia, kuidas muidu…
- Loome andmete kogumiseks tabelid.
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='Kandidaadid blokeerimiseks' COLLATE='utf8_general_ci' ENGINE=InnoDB AUTO_INCREMENT=1;Loo tabel `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='Juba blokeeritud aadresside loend' COLLATE='utf8_general_ci' ENGINE=InnoDB AUTO_INCREMENT=59;Loo tabel `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='Kõigi päringute ajalugu debugimiseks' COLLATE='utf8_general_ci' ENGINE=InnoDB AUTO_INCREMENT=1; - Loome faili, kuhu paigutame koodi. Kood salvestab adresseerimistabelisse ja hoiab ajalugu debugimiseks.
Faili kood IP aadresside salvestamiseks
<?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);Koodi olemus seisneb selles, et saada külastaja IP aadress ja salvestada see tabelisse. Kui IP juba olemas tabelis, siis suurendatakse välja cnt (päringute arvu veebisaidile)
- Nüüd see hirmus… Praegu põletavad mind minu teod 🙂
Kuna iga saidile pöördumise salvestamiseks on vaja, lisame faili koodi WordPressi peafaili — wp-load.php. Jah, me muuda põhifaili ja just pärast seda, kui globaalne muutujat $wpdb juba eksisteerib.
Nüüd näeme, kui sageli tuvastatakse iga IP-aadress meie tabelis, ja nautides kohvekruusi, vaatame sinna iga 5 minuti tagant, et aru saada olukorrast.

Jätkame lihtsalt: kopeerime „kahjuliku” IP, avame faili .htaccess ja lisame faili lõppu.
Order allow,deny
Allow from all
# start_auto_deny_list
Deny from 94.242.55.248
# end_auto_deny_list
Kõik! Nüüd ei pääse 94.242.55.248 veebisaidile ega genereeri andmebaasi koormust.
Kuid iga kord käsitsi kopeerimine ei ole just kõige rodustavam tegevus, lisaks sellele, et kood on mõeldud autonoomseks.
Lisame faili, mis täidetakse CRONi kaudu iga 30 minuti tagant:
Faili kood, mis muudab .htaccess
get_results("SELECT * FROM wp_visiters_bot WHERE cnt>{$limit_cnt}");
$new_blocked = [];
$exclude_ip = [
'87.236.16.70' //hostingu aadress
];
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;
}
//Tabeli puhastamine
$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, et sisestada .htaccess faili$target_content = false; //Flagi vajaliku koodilõigu jaokswhile (($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 "Viga: fgets() katse ebaõnnestus"; } fclose($handle); }//Praegune .htaccess fail $content = file_get_contents($file);$content = str_replace($replace_string, '', $content);//Puhastame kõik blokeeringud .htaccess failis file_put_contents($file, $content);//Uute blokeeringute kirjutamine $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))); Faili kood on piisavalt lihtne ja primitiivne, ning selle põhieesmärk on võtta blokeerimise kandidaate ja kirjutada blokeerimise reeglid faili .htaccess kommentaaride vahele.
# start_auto_deny_list и # end_auto_deny_list
Nüüd blokitakse „kahjulikud“ IP-d automaatselt ning fail .htaccess näeb välja umbes nii:
# 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
Koodi käivitamisel on võimalik näha tulemust hostingu paneelilt:

PS: Materjal on autoriteetne, kuigi osa sellest on mul juba minu veebilehel, siis Habres tuli välja ulatuslikum versioon.
Allikas: habr.com
