Laskemme mahdolliset "pahat" robotit ja estämme ne IP:n perusteella

Laskemme mahdolliset "pahat" robotit ja estämme ne IP:n perusteella

Hyvää päivää! Artikkelissa kerron sinulle, kuinka tavallisen isännöinnin käyttäjät voivat napata IP-osoitteita, jotka tuottavat liikaa kuormitusta sivustolle ja sitten estää ne isännöintityökaluilla, siellä on "hieman" php-koodia, muutama kuvakaappaus.

Syöttötiedot:

  1. Verkkosivusto luotu CMS WordPressillä
  2. Hosting Beget (tämä ei ole mainos, mutta hallintapaneelin kuvakaappaukset ovat tältä tietyltä hosting-palveluntarjoajalta)
  3. WordPress-sivusto julkaistiin jossain vuoden 2000 alussa, ja siellä on suuri määrä artikkeleita ja materiaaleja
  4. PHP versio 7.2
  5. WP:llä on uusin versio
  6. Sivusto alkoi jo jonkin aikaa tuottaa suurta kuormitusta MySQL:lle isännöintitietojen mukaan. Joka päivä tämä arvo ylitti 120 % tilikohtaisesta normista
  7. Yandexin mukaan. Metrica-sivustolla vierailee 100-200 ihmistä päivässä

Ensinnäkin tehtiin näin:

  1. Tietokantataulukot puhdistettiin kertyneestä roskista
  2. Tarpeettomat laajennukset poistettiin käytöstä, osia vanhentuneesta koodista poistettiin

Samalla haluan kiinnittää huomionne siihen, että välimuistivaihtoehtoja (välimuistilaajennuksia) kokeiltiin, havaintoja tehtiin - mutta 120%:n kuormitus yhdestä sivustosta pysyi ennallaan ja saattoi vain kasvaa.

Miltä hosting-tietokantojen arvioitu kuormitus näytti

Laskemme mahdolliset "pahat" robotit ja estämme ne IP:n perusteella
Ylhäällä on kyseinen sivusto, alapuolella on muita sivustoja, joilla on samat cm:t ja suunnilleen sama liikenne, mutta jotka kuormittavat vähemmän.

Analyysi

  • Datan välimuistivaihtoehdoilla tehtiin monia yrityksiä, havaintoja tehtiin useiden viikkojen aikana (onneksi isännöitsijä ei tänä aikana kirjoittanut minulle, että olen niin huono ja yhteys katkeaisi)
  • Hitaille kyselyille tehtiin analyysi ja haku, jonka jälkeen tietokannan rakennetta ja taulukkotyyppiä muutettiin hieman
  • Analyysissä käytimme ensisijaisesti sisäänrakennettuja AWStatteja (se muuten auttoi laskemaan huonoimman IP-osoitteen liikenteen määrän perusteella
  • Mittari - mittari tarjoaa tietoja vain ihmisistä, ei roboteista
  • WP:lle on yritetty käyttää laajennuksia, jotka voivat suodattaa ja estää vierailijoita jopa sijaintimaan ja erilaisten yhdistelmien mukaan
  • Täysin radikaali tapa osoittautui sulkea sivusto päiväksi merkinnällä "Olemme kunnossa" - tämä tehtiin myös kuuluisalla laajennuksella. Tässä tapauksessa odotamme kuormituksen laskevan, mutta emme nolla-arvoihin, koska WP-ideologia perustuu koukkuihin ja liitännäiset aloittavat toimintansa, kun "koukku" tapahtuu, ja ennen kuin "koukku" tapahtuu, pyynnöt tietokantaan voivat olla jo tehty

Ajatus

  1. Laske IP-osoitteet, jotka tekevät paljon pyyntöjä lyhyessä ajassa.
  2. Kirjaa sivustolle osumien määrä
  3. Estä pääsy sivustolle osumien määrän perusteella
  4. Estä käyttämällä .htaccess-tiedoston Deny from -merkintää
  5. En harkinnut muita vaihtoehtoja, kuten iptablesia ja Nginxin sääntöjä, koska kirjoitan isännöinnistä

Idea on ilmestynyt, joten se on toteutettava, koska ilman tätä...

  • Taulukoiden luominen tietojen keräämiseksi
    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;
    
  • Luodaan tiedosto, johon sijoitamme koodin. Koodi tallentuu estoehdokastaulukoihin ja säilyttää historian virheenkorjausta varten.

    Tiedostokoodi IP-osoitteiden tallentamista varten

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

    Koodin ydin on saada vierailijan IP-osoite ja kirjoittaa se taulukkoon. Jos ip on jo taulukossa, cnt-kenttää kasvatetaan (sivustolle saapuvien pyyntöjen määrä)

  • Nyt se pelottava asia... Nyt he polttavat minut teoistani :)
    Tallentaaksemme jokaisen pyynnön sivustolle yhdistämme tiedostokoodin WordPressin päätiedostoon - wp-load.php. Kyllä, vaihdamme ydintiedostoa ja juuri sen jälkeen, kun globaali muuttuja $wpdb on jo olemassa

Joten nyt voimme nähdä kuinka usein tämä tai tuo IP-osoite on merkitty taulukkoomme ja kahvikupin kanssa katsomme sinne 5 minuutin välein ymmärtääksemme kuvan

Laskemme mahdolliset "pahat" robotit ja estämme ne IP:n perusteella

Kopioi sitten "haitallinen" IP-osoite, avaa .htaccess-tiedosto ja lisää se tiedoston loppuun.

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

Siinä kaikki, nyt 94.242.55.248 - ei pääse sivustoon eikä kuormita tietokantaa

Mutta joka kerta tällainen käsin kopioiminen ei ole kovin oikeudenmukainen tehtävä, ja lisäksi koodin oli tarkoitus olla itsenäinen

Lisätään tiedosto, joka suoritetaan CRONin kautta 30 minuutin välein:

Tiedostokoodi muokkaa .htaccess-tiedostoa

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

Tiedoston koodi on melko yksinkertainen ja alkeellinen ja sen pääideana on ottaa ehdokkaita estoon ja kirjoittaa estosäännöt .htaccess-tiedostoon kommenttien väliin
# start_auto_deny_list ja # end_auto_deny_list

Nyt "haitalliset" IP:t estetään itsestään, ja .htaccess-tiedosto näyttää suunnilleen tältä:

# 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

Tämän seurauksena, kun tämä koodi alkaa toimia, voit nähdä tuloksen isännöintipaneelissa:

Laskemme mahdolliset "pahat" robotit ja estämme ne IP:n perusteella

PS: Materiaali on kirjoittajan omaa, vaikka julkaisinkin osan siitä verkkosivullani, sain laajemman version Habresta.

Lähde: will.com

Lisää kommentti