Chúng tôi xác định các bot “ác” tiềm năng và chặn chúng bằng IP

Chúng tôi xác định các bot “ác” tiềm năng và chặn chúng bằng IP

Ngày tốt! Trong bài viết tôi sẽ cho bạn biết cách người dùng lưu trữ thông thường có thể bắt các địa chỉ IP tạo ra tải quá mức trên trang web và sau đó chặn chúng bằng các công cụ lưu trữ, sẽ có “một chút” mã php, một vài ảnh chụp màn hình.

Dữ liệu đầu vào:

  1. Trang web được tạo trên CMS WordPress
  2. Hosting Beget (đây không phải là quảng cáo, nhưng ảnh chụp màn hình bảng quản trị sẽ từ nhà cung cấp dịch vụ lưu trữ cụ thể này)
  3. Trang WordPress được ra mắt đâu đó vào đầu năm 2000 và có số lượng bài viết, tài liệu rất lớn
  4. PHP phiên bản 7.2
  5. WP có phiên bản mới nhất
  6. Hiện tại, trang web đã bắt đầu tạo ra tải trọng cao trên MySQL theo dữ liệu lưu trữ. Mỗi ngày giá trị này vượt quá 120% định mức cho mỗi tài khoản
  7. Theo Yandex. Trang web Metrica được 100-200 người truy cập mỗi ngày

Trước hết, điều này đã được thực hiện:

  1. Các bảng cơ sở dữ liệu đã được dọn sạch rác tích lũy
  2. Các plugin không cần thiết đã bị vô hiệu hóa, các phần mã lỗi thời đã bị xóa

Đồng thời, tôi muốn bạn chú ý đến thực tế là các tùy chọn bộ đệm (plugin bộ đệm) đã được thử, quan sát đã được thực hiện - nhưng tải 120% từ một trang web không thay đổi và chỉ có thể tăng lên.

Tải gần đúng trên cơ sở dữ liệu lưu trữ trông như thế nào

Chúng tôi xác định các bot “ác” tiềm năng và chặn chúng bằng IP
Ở trên cùng là trang web được đề cập, ngay bên dưới là các trang web khác có cùng số cm và lưu lượng truy cập gần giống nhau nhưng tạo ra ít tải hơn.

Phân tích

  • Nhiều nỗ lực đã được thực hiện với các tùy chọn bộ nhớ đệm dữ liệu, việc quan sát được thực hiện trong vài tuần (may mắn thay, trong thời gian này, dịch vụ lưu trữ chưa bao giờ viết cho tôi rằng tôi quá tệ và sẽ bị ngắt kết nối)
  • Có phân tích và tìm kiếm truy vấn chậm, sau đó cấu trúc cơ sở dữ liệu và loại bảng có thay đổi một chút
  • Để phân tích, chúng tôi chủ yếu sử dụng AWStats tích hợp (nhân tiện, nó giúp tính toán địa chỉ IP kém nhất dựa trên lưu lượng truy cập
  • Số liệu - số liệu chỉ cung cấp thông tin về con người chứ không phải về bot
  • Đã có những nỗ lực sử dụng plugin cho WP có thể lọc và chặn khách truy cập ngay cả theo quốc gia vị trí và các kết hợp khác nhau
  • Một cách hoàn toàn triệt để hóa ra là đóng trang web trong một ngày với thông báo “Chúng tôi đang bảo trì” - việc này cũng được thực hiện bằng cách sử dụng plugin nổi tiếng. Trong trường hợp này, chúng tôi hy vọng tải sẽ giảm, nhưng không về giá trị 0, vì hệ tư tưởng WP dựa trên các hook và plugin bắt đầu hoạt động khi một “hook” xảy ra và trước khi “hook” xảy ra, các yêu cầu tới cơ sở dữ liệu có thể đã được thực hiện

Ý tưởng

  1. Tính toán các địa chỉ IP thực hiện nhiều yêu cầu trong một khoảng thời gian ngắn.
  2. Ghi lại số lượt truy cập vào trang web
  3. Chặn quyền truy cập vào trang web dựa trên số lượt truy cập
  4. Chặn bằng mục nhập “Từ chối” trong tệp .htaccess
  5. Tôi đã không xem xét các lựa chọn khác, như iptables và quy tắc cho Nginx, vì tôi đang viết về việc lưu trữ

Một ý tưởng đã xuất hiện nên cần phải thực hiện, nếu không có ý tưởng này...

  • Tạo bảng để tích lũy dữ liệu
    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;
    
  • Hãy tạo một tệp mà chúng ta sẽ đặt mã vào đó. Mã sẽ ghi vào các bảng ứng cử viên chặn và lưu lại lịch sử để gỡ lỗi.

    Mã tập tin để ghi địa chỉ 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);
    
    

    Bản chất của mã là lấy địa chỉ IP của khách truy cập và ghi nó vào bảng. Nếu ip đã có trong bảng thì trường cnt sẽ tăng lên (số lượng request tới site)

  • Bây giờ điều đáng sợ... Bây giờ họ sẽ thiêu sống tôi vì hành động của tôi :)
    Để ghi lại từng yêu cầu đến trang web, chúng tôi kết nối mã tệp với tệp WordPress chính - wp-load.php. Có, chúng tôi thay đổi tệp kernel và chính xác là sau khi biến toàn cục $wpdb đã tồn tại

Vì vậy, bây giờ chúng ta có thể thấy tần suất địa chỉ IP này hoặc địa chỉ IP kia được đánh dấu trong bảng của chúng ta và với một tách cà phê, chúng ta nhìn vào đó 5 phút một lần để hiểu bức tranh

Chúng tôi xác định các bot “ác” tiềm năng và chặn chúng bằng IP

Sau đó chỉ cần copy IP “có hại”, mở file .htaccess và thêm vào cuối file

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

Vậy là xong, hiện tại 94.242.55.248 - không có quyền truy cập vào trang web và không tạo tải trên cơ sở dữ liệu

Nhưng mỗi lần sao chép bằng tay như thế này không phải là một việc làm chính đáng cho lắm, hơn nữa, mã đó nhằm mục đích tự chủ

Hãy thêm một tệp sẽ được thực thi qua CRON cứ sau 30 phút:

Sửa đổi mã tệp .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)));

Mã tệp khá đơn giản và nguyên thủy và ý tưởng chính của nó là lấy các ứng cử viên để chặn và nhập quy tắc chặn trong tệp .htaccess giữa các bình luận
# start_auto_deny_list và # end_auto_deny_list

Giờ đây, các IP “có hại” đã bị chặn và tệp .htaccess trông giống như thế này:

# 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

Kết quả là sau khi mã này bắt đầu hoạt động, bạn có thể xem kết quả trong bảng lưu trữ:

Chúng tôi xác định các bot “ác” tiềm năng và chặn chúng bằng IP

Tái bút: Tài liệu là của tác giả, mặc dù tôi đã xuất bản một phần trên trang web của mình nhưng tôi đã nhận được phiên bản mở rộng hơn trên Habre.

Nguồn: www.habr.com

Thêm một lời nhận xét