Calculamos potenciais bots "maus" e os bloqueamos por IP



Bom dia No artigo, mostrarei como é possível para usuários comuns de hospedagem capturar endereços IP que geram carga excessiva no site e depois bloqueá-los usando ferramentas de hospedagem; haverá "um pouco" de código php, algumas capturas de tela.

Dados de entrada:

  1. Site criado no CMS WordPress
  2. Hosting Beget (este não é um anúncio, mas as capturas de tela do painel de administração serão deste provedor de hospedagem)
  3. O site WordPress foi lançado em algum lugar no início de 2000 e possui um grande número de artigos e materiais.
  4. Versão do PHP 7.2
  5. WP tem a versão mais recente
  6. Já faz algum tempo que o site começou a gerar uma alta carga no MySQL de acordo com a hospedagem. Todos os dias, esse valor excede 120% da norma por conta
  7. De acordo com Yandex. O site métrico é visitado por 100 a 200 pessoas por dia

Primeiro de tudo, foi feito:

  1. Os bancos de dados são limpos de lixo acumulado
  2. Plugins desnecessários estão desabilitados, seções de código desatualizado são removidas

Ao mesmo tempo, chamo a atenção para as opções de cache (plugins de cache), fizemos observações - mas a carga de 120% de um site permaneceu inalterada e só pôde crescer.

Como era a carga aproximada nos bancos de dados de hospedagem


No topo está o site em questão, logo abaixo de outros sites que têm o mesmo cms e aproximadamente o mesmo tráfego, mas criam menos carga.

Análise

  • Muitas tentativas foram feitas com opções para armazenar dados em cache, observações foram feitas por várias semanas (o benefício de hospedar durante esse período nunca escrevi que sou tão ruim e me desconectou)
  • Houve uma análise e pesquisa de consultas lentas, depois a estrutura do banco de dados e o tipo de tabela foram ligeiramente alterados
  • Para análise, o AWStats interno foi usado principalmente (a propósito, ajudou a calcular o endereço IP mais ruim por volume de tráfego
  • Métrica - a métrica fornece informações apenas sobre pessoas, não sobre bots
  • Houve tentativas de usar plug-ins para o WP, que podem filtrar e bloquear os visitantes mesmo por país de localização e por várias combinações
  • Uma maneira completamente radical acabou sendo fechar o site por um dia com a marca “Estamos em manutenção” - isso também foi feito usando o famoso plug-in. Nesse caso, esperamos que a carga caia, mas não para os valores à esquerda de 0, já que a ideologia do WP é baseada em ganchos e plugins iniciam sua atividade quando ocorre algum tipo de "gancho" e, antes que o "gancho" ocorra, consultas ao banco de dados já podem ser feitas.

Idéia

  1. Calcule endereços IP que fazem muitas solicitações em um curto período de tempo.
  2. Corrija o número de visitas ao site
  3. Com base no número de acessos, bloqueia o acesso ao site
  4. Bloqueie usando a entrada "Negar de" no arquivo .htaccess
  5. Outras opções, como iptables e regras para o Nginx, sem considerar, porque eu escrevo sobre hospedagem

Uma ideia surgiu, então você tem que implementá-la, pois sem ela ...

  • Criamos tabelas para acumulação de dados

    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; 
  • Crie um arquivo no qual colocaremos o código. O código será escrito na tabela de candidatos para bloqueio e manterá um histórico para depuração.

    Código de arquivo para gravar endereços IP
     <?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); 


    A essência do código é obter o endereço IP do visitante e gravá-lo na tabela. Se o ip já estiver na tabela, o campo cnt será aumentado (o número de solicitações ao site)
  • Agora é assustador ... Agora eles vão me queimar por minhas ações :)
    Para gravar todas as chamadas para o site, incluímos o código do arquivo no arquivo principal do WordPress - wp-load.php. Sim, modificamos o arquivo do kernel precisamente depois que a variável global $ wpdb já existe

Agora, podemos ver com que frequência esse ou aquele endereço IP é anotado em nossa tabela e, com uma xícara de café, olhamos para lá a cada 5 minutos para entender a imagem.



Em seguida, basta copiar o IP "nocivo", abrir o arquivo .htaccess e adicioná-lo ao final do arquivo

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

Tudo, agora 94.242.55.248 - não tem acesso ao site e não gera uma carga no banco de dados

Mas cada vez que copiar com suas próprias mãos não é uma tarefa muito justa e, além disso, o código foi concebido como autônomo

Adicione um arquivo que será executado pelo CRON a cada 30 minutos:

Código do arquivo que modifica .htaccess
 <?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))); 


O código do arquivo é bastante simples e primitivo e sua principal idéia é levar candidatos para o bloqueio e inserir as regras de bloqueio no arquivo .htaccess entre comentários
# start_auto_deny_list e # end_auto_deny_list

Agora, o ip "nocivo" é bloqueado por eles mesmos e o arquivo .htaccess se parece com isso:

 # 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 

Como resultado, após o início da ação desse código, você pode ver o resultado no painel de hospedagem:



PS: O material é protegido por direitos autorais, embora eu tenha publicado parte dele no meu site, mas no Habre uma versão mais expandida foi obtida.

Source: https://habr.com/ru/post/pt473030/


All Articles