Head päeva! Artiklis räägin teile, kuidas tavalise hostimise kasutajad saavad saidil liigset koormust tekitavaid IP-aadresse kinni püüda ja seejärel hostimistööriistade abil blokeerida, ilmub "natuke" php-koodi, paar ekraanipilti.
Sisendandmed:
- Veebileht loodud CMS WordPressis
- Begeti hostimine (see ei ole reklaam, vaid administraatoripaneeli ekraanipildid pärinevad sellelt konkreetselt hostiteenuse pakkujalt)
- WordPressi sait käivitati kuskil 2000. aasta alguses ning sellel on palju artikleid ja materjale
- PHP versioon 7.2
- WP-l on uusim versioon
- Juba mõnda aega hakkas sait hostimisandmete põhjal MySQL-ile suurt koormust tekitama. Iga päev ületas see väärtus 120% konto normist
- Yandexi andmetel. Metrica saiti külastab 100-200 inimest päevas
Esiteks tehti seda:
- Andmebaasi tabelid puhastati kogunenud prügist
- Mittevajalikud pistikprogrammid keelati, aegunud koodi osad eemaldati
Samas juhin teie tähelepanu asjaolule, et prooviti vahemällu salvestamise võimalusi (vahemällu salvestamise pluginaid), tehti vaatlusi - kuid 120% koormus ühelt saidilt jäi muutumatuks ja sai ainult kasvada.
Kuidas nägi välja ligikaudne andmebaaside hostimise koormus
Ülaosas on kõnealune sait, veidi all teised saidid, millel on samad cm-d ja ligikaudu sama liiklus, kuid mis tekitavad vähem koormust.
Analüüs
- Andmete vahemällu salvestamise võimalustega tehti palju katseid, vaatlusi tehti mitme nädala jooksul (õnneks ei kirjutanud hostimine mulle selle aja jooksul kordagi, et mul on nii halb ja ühendus katkeb)
- Toimus aeglaste päringute analüüs ja otsing, seejärel muudeti veidi andmebaasi struktuuri ja tabelitüüpi
- Analüüsiks kasutasime eelkõige sisseehitatud AWStatsi (see aitas muuseas liiklusmahu põhjal arvutada halvima IP-aadressi
- Mõõdik – mõõdik annab teavet ainult inimeste, mitte robotite kohta
- WP jaoks on üritatud kasutada pluginaid, mis suudavad külastajaid filtreerida ja blokeerida isegi asukohariigi ja erinevate kombinatsioonide järgi
- Täiesti radikaalseks viisiks osutus saidi päevaks sulgemine märkega “Oleme hoolduses” - seda tehti ka kuulsa pistikprogrammi abil. Sel juhul eeldame, et koormus langeb, kuid mitte nullväärtustele, kuna WP ideoloogia põhineb konksudel ja pistikprogrammid alustavad oma tegevust „konksu” ilmnemisel ning enne „konksu” tekkimist saavad päringud andmebaasile juba tehtud
Mõte
- Arvutage IP-aadressid, mis esitavad lühikese aja jooksul palju päringuid.
- Salvestage saidi tabamuste arv
- Blokeeri juurdepääs saidile tabamuste arvu alusel
- Blokeerige, kasutades .htaccess-failis kirjet „Keela
- Ma ei kaalunud muid võimalusi, nagu iptables ja Nginxi reeglid, sest kirjutan hostimisest
Idee on ilmunud, nii et see tuleb ellu viia, kuna ilma selleta ...
- Tabelite loomine andmete kogumiseks
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;
- Loome faili, kuhu asetame koodi. Kood salvestatakse blokeerimiskandidaatide tabelitesse ja säilitab silumiseks ajaloo.
Failikood 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 põhiolemus on hankida külastaja IP-aadress ja kirjutada see tabelisse. Kui ip on juba tabelis, suurendatakse cnt välja (saidile saadetavate päringute arv)
- Nüüd see hirmus asi... Nüüd põletavad nad mu tegude pärast ära :)
Iga saidi päringu salvestamiseks ühendame faili koodi WordPressi põhifailiga - wp-load.php. Jah, me muudame kerneli faili ja täpselt pärast seda, kui globaalne muutuja $wpdb on juba olemas
Nii, nüüd näeme, kui sageli see või teine IP-aadress meie tabelisse märgitud on ja kohvitassiga vaatame korra 5 minuti tagant sinna, et pildist aru saada
Seejärel kopeerige lihtsalt "kahjulik" IP, avage .htaccess-fail ja lisage see faili lõppu
Order allow,deny
Allow from all
# start_auto_deny_list
Deny from 94.242.55.248
# end_auto_deny_list
See on kõik, nüüd 94.242.55.248 - tal pole saidile juurdepääsu ja see ei koorma andmebaasi
Kuid iga kord niisugune käsitsi kopeerimine ei ole kuigi õige ülesanne ja pealegi oli kood mõeldud autonoomseks
Lisame faili, mis käivitatakse CRON-i kaudu iga 30 minuti järel:
Failikood, mis muudab .htaccessi
<?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)));
Faili kood on üsna lihtne ja primitiivne ning selle põhiidee on võtta blokeerimise kandidaadid ja sisestada blokeerimisreeglid .htaccess faili kommentaaride vahele
# start_auto_deny_list ja # end_auto_deny_list
Nüüd on "kahjulikud" IP-d iseenesest blokeeritud ja .htaccess-fail näeb välja umbes selline:
# 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
Selle tulemusel näete tulemust pärast selle koodi tööle hakkamist hostimispaneelil:
PS: materjal on autori oma, kuigi avaldasin osa sellest oma veebisaidil, sain Habre kohta laiendatud versiooni.
Allikas: www.habr.com