Vi identifierar potentiella "onda" bots och blockerar dem efter IP

Vi identifierar potentiella "onda" bots och blockerar dem efter IP

God dag! I den här artikeln kommer jag att berätta hur användare av vanlig webbhotell kan fånga IP-adresser som orsakar överdriven belastning på webbplatsen och sedan blockera dem med hjälp av webbhotellens verktyg. Det kommer att vara lite PHP-kod och några skärmdumpar.

Inledande uppgifter:

  1. Webbplatsen är skapad med CMS WordPress
  2. Hosting Beget (detta är ingen reklam, men skärmdumparna kommer att vara från den här webbhotell-leverantören)
  3. WordPress-webbplatsen startade någon gång i början av 2000-talet och har ett stort antal artiklar och material.
  4. PHP-version 7.2
  5. WP har den senaste versionen
  6. På senaste tiden har webbplatsen börjat generera hög belastning på MySQL, enligt webbhotellet. Varje dag överskred detta värde 120% av normer för kontot.
  7. Enligt Yandex.Metrika besöker 100-200 personer webbplatsen per dag.

Först och främst gjordes följande:

  1. Databastabellerna städades från ackumulerat skräp.
  2. Onödiga plugins stängdes av, och delar av föråldrad kod togs bort.

Samtidigt vill jag påpeka att olika caching-alternativ (caching-plugins) prövades, och observationer gjordes – men belastningen på 120% från en enda webbplats förblev oförändrad och kunde bara öka.

Så här såg den ungefärliga belastningen på databaserna hos webbhotellet ut.

Vi identifierar potentiella "onda" bots och blockerar dem efter IP
Den aktuella webbplatsen är i topp, strax nedanför finns andra webbplatser med samma CMS och ungefär samma besöksantal, men som skapar mindre belastning.

Analys

  • Många försök gjordes med olika caching-alternativ, och observationer gjordes under flera veckor (luckligt nog har webbhotellet under denna tid aldrig skrivit till mig att jag är så dålig och att jag kommer att stängas av).
  • En analys och sökning efter långsamma förfrågningar gjordes, och därefter justerades databasstrukturen och tabelltyperna något.
  • För analys användes i första hand den inbyggda AWStats (den hjälpte för övrigt till att identifiera den mest skadliga IP-adressen baserat på trafikvolym).
  • Metrika – metrikan ger information om människor, inte om bots.
  • Försök gjordes att använda plugins för WP som kan filtrera och blockera besökare även baserat på deras ursprungsland och olika kombinationer.
  • Ett helt radikalt sätt att stänga ner webbplatsen i ett dygn med meddelandet "Vi är under tekniskt underhåll" - det gjordes också med hjälp av den berömda pluginen. I detta fall förväntades belastningen minska, men inte till 0, eftersom WP:s ideologi bygger på hooks och plugins börjar sin aktivitet vid något "hook", och före dess kan det redan ha gjorts förfrågningar till databasen.

Tanken

  1. Beräkna IP-adresser som gör många förfrågningar på kort tid.
  2. Registrera antalet förfrågningar till webbplatsen.
  3. Baserat på antalet förfrågningar blockera åtkomsten till webbplatsen.
  4. Blockera med hjälp av "Deny from" i .htaccess-filen.
  5. Alternativa metoder som iptables och regler för Nginx övervägs inte, eftersom jag skriver om webbhotell.

Idén har fötts, så nu måste den realiseras, hur annars…

  • Skapa tabeller för att samla in data.
    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='Kandidater för blockering'
    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 över redan blockerade'
    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 över alla förfrågningar för debug'
    COLLATE='utf8_general_ci'
    ENGINE=InnoDB
    AUTO_INCREMENT=1;
    
  • Skapa en fil som vi kommer att placera koden i. Koden kommer att registrera kandidater för blockering och hålla en historia för debug.

    Kod för filen för att registrera IP-adresser.

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

    Syftet med koden är att få besökarens IP-adress och registrera det i tabellen. Om IP-adressen redan finns i tabellen kommer fältet cnt (antal förfrågningar till webbplatsen) att ökas.

  • Nu det skrämmande… Nu kommer de att bränna mig för mina handlingar 🙂
    För att registrera varje förfrågan till webbplatsen kopplar vi in koden från filen i huvudfilen för WordPress - wp-load.php. Ja, vi ändrar kärnfilen och detta görs först efter att den globala variabeln $wpdb redan existerar.

Nu kan vi se hur ofta en viss IP-adress anges i vår tabell, och vi kollar där med en kopp kaffe var femte minut för att förstå läget.

Vi identifierar potentiella "onda" bots och blockerar dem efter IP

Därefter kopierade vi helt enkelt den "skadliga" IP-adressen, öppnade .htaccess-filen och lade till den i slutet av filen.

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

Nu har 94.242.55.248 ingen åtkomst till webbplatsen och generar ingen belastning på databasen.

Men att kopiera detta för hand varje gång är inte särskilt praktiskt, och dessutom var koden tänkt att vara autonom.

Vi lägger till en fil som kommer att köras via CRON var 30:e minut:

Koden för att modifiera .htaccess

get_results("SELECT * FROM wp_visiters_bot WHERE cnt>{$limit_cnt}");

$new_blocked = [];

$exclude_ip = [
    '87.236.16.70' // värdaddress
];

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;
}

// Rensa tabellen
$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 för att infoga i .htaccess-filen$target_content = false; // Flagga för den del av koden vi behöverwhile (($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 "Fel: fgets() misslyckades oväntat"; } fclose($handle); }// Aktuell .htaccess-fil $content = file_get_contents($file);$content = str_replace($replace_string, '', $content);// Rensa alla blockeringar i .htaccess-filen file_put_contents($file, $content);// Skriv nya blockeringar $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)));

Koden för filen är ganska enkel och primitiv, och dess huvudidé är att ta kandidater för blockering och skriva in blockeringsreglerna i .htaccess-filen mellan kommentarerna.
# start_auto_deny_list и # end_auto_deny_list

Nu blockeras de "skadliga" IP-adresserna automatiskt, och .htaccess-filen ser ut ungefär så här:

# 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

Efter att koden har aktiverats kan man se resultatet i hostingpanelen:

Vi identifierar potentiella "onda" bots och blockerar dem efter IP

PS: Materialet är originellt, även om jag har publicerat delar av det på min egen webbplats så fick det en mer omfattande version på Habre.

Källa: habr.com

Köp pålitlig webbhotell med DDoS-skydd, VPS VDS-servrar 🔥 Köp pålitlig webbhotell med DDoS-skydd, VPS VDS-servrar | ProHoster