Wir identifizieren potenzielle „böse“ Bots und blockieren sie per IP

Wir identifizieren potenzielle „böse“ Bots und blockieren sie per IP

Guten Tag! In dem Artikel erzähle ich Ihnen, wie Benutzer von regulärem Hosting IP-Adressen abfangen können, die eine übermäßige Auslastung der Website verursachen, und diese dann mithilfe von Hosting-Tools blockieren können. Es wird „ein bisschen“ PHP-Code geben, ein paar Screenshots.

Eingabedaten:

  1. Website erstellt auf CMS WordPress
  2. Hosting Beget (dies ist keine Werbung, aber die Screenshots des Admin-Panels stammen von diesem bestimmten Hosting-Anbieter)
  3. Die WordPress-Site wurde Anfang 2000 gestartet und verfügt über eine große Anzahl von Artikeln und Materialien
  4. PHP-Version 7.2
  5. WP hat die neueste Version
  6. Den Hosting-Daten zufolge erzeugte die Site seit einiger Zeit eine hohe Auslastung von MySQL. Jeden Tag überstieg dieser Wert 120 % der Norm pro Konto
  7. Laut Yandex. Die Metrica-Website wird täglich von 100 bis 200 Personen besucht

Dies wurde zunächst einmal gemacht:

  1. Datenbanktabellen wurden vom angesammelten Müll befreit
  2. Unnötige Plugins wurden deaktiviert, Teile veralteten Codes entfernt

Gleichzeitig möchte ich Sie darauf aufmerksam machen, dass Caching-Optionen (Caching-Plugins) ausprobiert und Beobachtungen gemacht wurden – die Auslastung von 120 % von einer Site blieb jedoch unverändert und konnte nur wachsen.

Wie die ungefähre Belastung der Hosting-Datenbanken aussah

Wir identifizieren potenzielle „böse“ Bots und blockieren sie per IP
Oben steht die betreffende Site, direkt darunter sind andere Sites, die das gleiche CMS und ungefähr den gleichen Traffic haben, aber weniger Last erzeugen.

Analyse

  • Es wurden viele Versuche mit Daten-Caching-Möglichkeiten unternommen, Beobachtungen wurden über mehrere Wochen durchgeführt (zum Glück hat mir das Hosting in dieser Zeit nie geschrieben, dass es mir so schlecht geht und die Verbindung getrennt werden würde)
  • Es erfolgte eine Analyse und Suche nach langsamen Abfragen, anschließend wurden die Datenbankstruktur und der Tabellentyp leicht geändert
  • Für die Analyse haben wir in erster Linie das integrierte AWStats verwendet (übrigens hat es geholfen, die schlechteste IP-Adresse basierend auf dem Verkehrsaufkommen zu berechnen).
  • Metrik – die Metrik liefert nur Informationen über Personen, nicht über Bots
  • Es gab Versuche, Plugins für WP zu verwenden, die Besucher sogar nach Standortland und verschiedenen Kombinationen filtern und blockieren können
  • Als völlig radikaler Weg erwies sich die Schließung der Seite für einen Tag mit dem Hinweis „Wir befinden uns in Wartung“ – auch dies erfolgte über das berühmte Plugin. In diesem Fall erwarten wir, dass die Last sinkt, jedoch nicht auf Nullwerte, da die WP-Ideologie auf Hooks basiert und Plugins ihre Aktivität beginnen, wenn ein „Hook“ auftritt, und bevor der „Hook“ auftritt, können Anfragen an die Datenbank gestellt werden bereits gemacht werden

Idee

  1. Berechnen Sie IP-Adressen, die in kurzer Zeit viele Anfragen stellen.
  2. Notieren Sie die Anzahl der Zugriffe auf die Website
  3. Blockieren Sie den Zugriff auf die Website basierend auf der Anzahl der Zugriffe
  4. Blockieren Sie mit dem Eintrag „Deny from“ in der .htaccess-Datei
  5. Andere Optionen wie iptables und Regeln für Nginx habe ich nicht in Betracht gezogen, da ich über Hosting schreibe

Eine Idee ist aufgetaucht, also muss sie umgesetzt werden, denn ohne dies...

  • Erstellen von Tabellen zum Sammeln 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='Кандидаты для блокировки'
    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;
    
  • Erstellen wir eine Datei, in der wir den Code platzieren. Der Code wird in den Blockierungskandidatentabellen aufgezeichnet und speichert einen Verlauf zum Debuggen.

    Dateicode zur Aufzeichnung 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 Kern des Codes besteht darin, die IP-Adresse des Besuchers abzurufen und in eine Tabelle zu schreiben. Wenn die IP bereits in der Tabelle vorhanden ist, wird das cnt-Feld erhöht (die Anzahl der Anfragen an die Site).

  • Jetzt ist das Erschreckende... Jetzt werden sie mich für meine Taten verbrennen :)
    Um jede Anfrage an die Site aufzuzeichnen, verbinden wir den Dateicode mit der Hauptdatei von WordPress – wp-load.php. Ja, wir ändern die Kerneldatei und zwar genau dann, wenn die globale Variable $wpdb bereits existiert

Jetzt können wir sehen, wie oft diese oder jene IP-Adresse in unserer Tabelle markiert ist, und bei einer Tasse Kaffee schauen wir alle 5 Minuten dorthin, um das Bild zu verstehen

Wir identifizieren potenzielle „böse“ Bots und blockieren sie per IP

Dann kopieren Sie einfach die „schädliche“ IP, öffnen Sie die .htaccess-Datei und fügen Sie sie am Ende der Datei hinzu

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

Das ist alles, jetzt 94.242.55.248 – hat keinen Zugriff auf die Site und erzeugt keine Belastung der Datenbank

Aber jedes Mal ist es keine sehr gerechte Aufgabe, so von Hand zu kopieren, und außerdem sollte der Code autonom sein

Fügen wir eine Datei hinzu, die alle 30 Minuten über CRON ausgeführt wird:

Dateicode, der .htaccess ändert

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

Der Dateicode ist recht einfach und primitiv und seine Hauptidee besteht darin, Kandidaten zum Blockieren zu nehmen und zwischen den Kommentaren Blockierungsregeln in die .htaccess-Datei einzugeben
# start_auto_deny_list und # end_auto_deny_list

Jetzt werden „schädliche“ IPs von selbst blockiert und die .htaccess-Datei sieht in 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

Nachdem dieser Code funktioniert, können Sie das Ergebnis im Hosting-Panel sehen:

Wir identifizieren potenzielle „böse“ Bots und blockieren sie per IP

PS: Das Material gehört dem Autor, obwohl ich einen Teil davon auf meiner Website veröffentlicht habe, habe ich eine erweiterte Version auf Habre erhalten.

Source: habr.com

Kommentar hinzufügen