
Guten Tag! In diesem Artikel erläutere ich, wie Benutzer eines Shared Hosting IP-Adressen erfassen können, die übermäßige Last auf der Website erzeugen, und wie man diese dann mithilfe der Hosting-Tools blockiert. Es wird ein wenig PHP-Code und einige Screenshots geben.
Eingabedaten:
- Website erstellt mit CMS WordPress
- Хостинг Beget (das ist keine Werbung, aber die Screenshots stammen von diesem Hosting-Anbieter)
- Die WordPress-Website wurde Anfang der 2000er Jahre gestartet und verfügt über eine große Anzahl von Artikeln und Materialien.
- PHP-Version 7.2
- WP hat die neueste Version.
- Seit einiger Zeit hat die Website eine hohe Last auf MySQL verursacht, gemäß den Angaben des Hostings. Täglich überschritt dieser Wert 120% des Normwerts für das Konto.
- Laut Yandex.Metrica besuchen täglich 100-200 Personen die Website.
Zunächst wurde Folgendes durchgeführt:
- Die Datenbanktabellen wurden von angesammeltem Müll bereinigt.
- Unnötige Plugins wurden deaktiviert, veralteter Code entfernt.
Ich möchte darauf hinweisen, dass verschiedene Optionen zur Cache-Implementierung (Caching-Plugins) ausprobiert wurden; es wurden Beobachtungen durchgeführt – aber die Last von 120% für eine einzige Website blieb gleich und konnte nur zunehmen.
So sah die ungefähre Last auf die Datenbanken des Hostings aus.

Die Website, um die es geht, steht ganz oben; darunter sind andere Websites, die dasselbe CMS verwenden und ähnliche Besucherzahlen haben, aber weniger Belastung erzeugen.
Analyse
- Es wurden viele Versuche mit verschiedenen Daten-Caching-Optionen unternommen, und es fanden Beobachtungen über mehrere Wochen statt (zum Glück hat der Hosting-Anbieter in dieser Zeit nie gesagt, dass ich so schlecht bin und abgeschaltet werde).
- Es wurde eine Analyse und Suche nach langsamen Abfragen durchgeführt, anschließend wurde die Struktur der Datenbank und der Tabellentyp leicht verändert.
- Für die Analyse wurde in erster Linie das integrierte AWStats verwendet (es hat übrigens geholfen, die bösartigste IP-Adresse nach dem Datenvolumen zu ermitteln).
- Die Metrik – die Metrik liefert Informationen nur über echte Nutzer und nicht über Bots.
- Es gab Versuche, Plugins für WP zu verwenden, die in der Lage sind, Besucher sogar nach ihrem Standort und verschiedenen Kombinationen zu filtern und zu blockieren.
- Eine ziemlich radikale Methode, um die Website für einen Tag mit dem Hinweis „Wir sind in Wartung“ zu schließen, wurde ebenfalls mithilfe des berühmten Plugins umgesetzt. In diesem Fall erwartete man, dass die Last sinkt, jedoch nicht auf null, da die Ideologie von WP auf Hooks basiert und Plugins ihre Aktivitäten bei Eintreten eines bestimmten „Hooks“ beginnen. Vor dem Eintreten eines „Hooks“ können jedoch bereits Anfragen an die Datenbank durchgeführt werden.
Die Idee
- Ermitteln Sie IP-Adressen, die in kurzer Zeit viele Anfragen stellen.
- Die Anzahl der Anfragen an die Website erfassen.
- Zugang zur Website basierend auf der Anzahl der Anfragen blockieren.
- Blockieren Sie mit dem Eintrag „Deny from“ in der .htaccess-Datei.
- Andere Optionen, wie iptables und Regeln für Nginx, lasse ich außen vor, da ich über Hosting schreibe.
Eine Idee ist entstanden, also muss sie umgesetzt werden, wie könnte es anders sein…
- Wir erstellen Tabellen zum Speichern von Daten.
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='Kandidaten für die Blockierung' COLLATE='utf8_general_ci' ENGINE=InnoDB AUTO_INCREMENT=1;ERSTELLE TABELLE `wp_visiters_bot_blocked` ( `id` INT(11) NICHT NULL AUTO_INCREMENT, `ip` VARCHAR(300) NICHT NULL, `data_update` DATETIME NULL STANDARD CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMÄRSCHLÜSSEL (`id`), EINZIGARTIGER INDEX `ip` (`ip`) ) COMMENT='Liste der bereits gesperrten' COLLATE='utf8_general_ci' ENGINE=InnoDB AUTO_INCREMENT=59;ERSTELLE TABELLE `wp_visiters_bot_history` ( `id` INT(11) NICHT NULL AUTO_INCREMENT, `ip` VARCHAR(300) NULL STANDARD NULL, `browser` VARCHAR(500) NULL STANDARD NULL, `cnt` INT(11) NULL STANDARD NULL, `data_update` DATETIME NULL STANDARD CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, `data_add` DATETIME NULL STANDARD CURRENT_TIMESTAMP, PRIMÄRSCHLÜSSEL (`id`), EINZIGARTIGER INDEX `ip` (`ip`) ) COMMENT='Historie aller Anfragen für Debugging' COLLATE='utf8_general_ci' ENGINE=InnoDB AUTO_INCREMENT=1; - Lass uns eine Datei erstellen, in der wir den Code unterbringen. Der Code wird die Einträge in die Tabellen der Kandidaten für die Sperrung und die Historie für das Debugging beinhalten.
Code der Datei zum Speichern von IP-Adressen
<?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);Der Sinn des Codes besteht darin, die IP-Adresse des Besuchers zu erhalten und sie in die Tabelle einzutragen. Wenn die IP bereits in der Tabelle vorhanden ist, wird das Feld cnt (Anzahl der Anfragen an die Website) erhöht.
- Jetzt wird's ernst... Jetzt werde ich für meine Taten verbrannt 🙂
Um jede Anfrage an die Webseite zu protokollieren, fügen wir den Code der Datei in die Hauptdatei von WordPress — wp-load.php — ein. Ja, wir ändern die Kern-Datei und das, nachdem die globale Variable $wpdb bereits existiert.
Jetzt können wir sehen, wie oft eine bestimmte IP-Adresse in unserer Tabelle markiert wird, und mit einer Tasse Kaffee schauen wir alle 5 Minuten rein, um die Situation zu verstehen.

Dann einfach, die "schadhafte" IP kopiert, die .htaccess-Datei geöffnet und ans Ende der Datei angefügt.
Order allow,deny
Allow from all
# start_auto_deny_list
Deny from 94.242.55.248
# end_auto_deny_list
Fertig, jetzt hat 94.242.55.248 keinen Zugriff auf die Website und erzeugt keine Last auf der Datenbank.
Aber jedes Mal von Hand zu kopieren, ist nicht wirklich die beste Methode, zudem wurde der Code für die Autonomie gedacht.
Fügen wir eine Datei hinzu, die alle 30 Minuten über CRON ausgeführt wird:
Code der Datei, die die .htaccess modifiziert
get_results("SELECT * FROM wp_visiters_bot WHERE cnt>{$limit_cnt}");
$new_blocked = [];
$exclude_ip = [
'87.236.16.70'//Hosting-Adresse
];
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;
}
//Tabelle bereinigen
$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 zum Einfügen in die .htaccess-Datei$target_content = false; //Flag für den betreffenden Codeabschnittwhile (($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 "Fehler: fgets() hat unerwartet versagt"; } fclose($handle); }//Aktuelle .htaccess-Datei $content = file_get_contents($file);$content = str_replace($replace_string, '', $content);//Beseitigung aller Blockierungen in der .htaccess-Datei file_put_contents($file, $content);//Aufzeichnung neuer Blockierungen $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))); Der Code ist recht einfach und primitiv. Die Hauptidee besteht darin, die Kandidaten für die Sperrung zu erfassen und die Sperrregeln in die .htaccess-Datei zwischen den Kommentaren einzufügen.
# start_auto_deny_list и # end_auto_deny_list
Nun werden die „schädlichen“ IPs automatisch blockiert, und die .htaccess-Datei sieht etwa so aus:
# 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
Nach dem Start dieses Codes kann man das Ergebnis im Hosting-Dashboard sehen:

PS: Das Material ist urheberrechtlich geschützt. Auch wenn ich einen Teil davon auf meiner Website veröffentlicht habe, wurde auf Habre eine erweiterte Version erstellt.
Quelle: habr.com
