Налаштування лімітів замовлень за IP-адресою 1С-Бітрікс

Наша компанія займається розробкою, підтримкою та обслуговуванням рішень на Бітрікс та Бітрікс24 будь-якої складності. Від простих односторінкових сайтів до складних інтернет-магазинів, CRM систем з інтеграцією 1С та телефонії. Досвід розробників підтверджено сертифікатами від вендора.
Пропоновані послуги
Показано 1 з 1 послугУсі 1626 послуг
Налаштування лімітів замовлень за IP-адресою 1С-Бітрікс
Проста
~1 робочий день
Часті питання

Наші компетенції:

Етапи розробки

Останні роботи

  • image_website-b2b-advance_0.png
    Розробка сайту компанії B2B ADVANCE
    1262
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Розробка веб-сайту для компанії ФІКСПЕР
    851
  • image_bitrix-bitrix-24-1c_development_of_an_online_appointment_booking_widget_for_a_medical_center_594_0.webp
    Розробка на базі Бітрікс, Бітрікс24, 1С для компанії Development of an Online
    585
  • image_bitrix-bitrix-24-1c_mirsanbel_458_0.webp
    Розробка на базі 1С Підприємство для компанії МИРСАНБЕЛ
    751
  • image_crm_dolbimby_434_0.webp
    Розробка сайту на CRM Бітрікс24 для компанії DOLBIMBY
    657
  • image_crm_technotorgcomplex_453_0.webp
    Розробка на базі Бітрікс24 для компанії ТЕХНОТОРГКОМПЛЕКС
    989

Налаштування лімітів замовлень по IP-адресі 1С-Бітрікс

Одна IP-підмережа, 50 замовлень за 10 хвилин — або масовий шахрайський вкид, або баг на фронтенді з повторним відправленням форми. В обох випадках ліміти по IP захищають базу даних від сміття і гаманець від втрат.

Ліміти на рівні PHP

Перевірка в обробнику події перед збереженням замовлення:

namespace Local\Fraud;

class IpOrderLimiter
{
    // Ліміти: [інтервал у хвилинах => максимум замовлень]
    private const LIMITS = [
        15  => 3,   // не більше 3 замовлень за 15 хвилин
        60  => 5,   // не більше 5 замовлень за 1 годину
        1440 => 15, // не більше 15 замовлень за добу
    ];

    public static function check(string $ip): ?string
    {
        $conn    = \Bitrix\Main\Application::getConnection();
        $ipSafe  = $conn->getSqlHelper()->forSql($ip);

        foreach (self::LIMITS as $minutes => $maxOrders) {
            $from = date('Y-m-d H:i:s', time() - $minutes * 60);

            $count = (int)$conn->query(
                "SELECT COUNT(*) cnt FROM b_sale_order
                 WHERE CREATED_BY_IP = '{$ipSafe}'
                   AND DATE_INSERT   >= '{$from}'"
            )->fetch()['cnt'];

            if ($count >= $maxOrders) {
                return "Перевищено ліміт замовлень з вашого IP. Спробуйте через " . self::cooldownMinutes($minutes, $maxOrders, $ip) . " хвилин.";
            }
        }

        return null;
    }

    private static function cooldownMinutes(int $windowMinutes, int $max, string $ip): int
    {
        $conn   = \Bitrix\Main\Application::getConnection();
        $ipSafe = $conn->getSqlHelper()->forSql($ip);
        $from   = date('Y-m-d H:i:s', time() - $windowMinutes * 60);

        // Знаходимо найраніше із останніх $max замовлень
        $oldest = $conn->query(
            "SELECT MIN(DATE_INSERT) dt FROM (
                SELECT DATE_INSERT FROM b_sale_order
                WHERE CREATED_BY_IP = '{$ipSafe}'
                  AND DATE_INSERT   >= '{$from}'
                ORDER BY DATE_INSERT ASC
                LIMIT {$max}
            ) sub"
        )->fetch()['dt'];

        if (!$oldest) return $windowMinutes;

        $oldestTs = strtotime($oldest);
        return max(1, (int)ceil(($oldestTs + $windowMinutes * 60 - time()) / 60));
    }
}

Обробник події:

AddEventHandler('sale', 'OnBeforeOrderFinalAction', function(\Bitrix\Sale\Order $order) {
    if ($order->getId() > 0) return new \Bitrix\Main\EventResult(\Bitrix\Main\EventResult::SUCCESS);

    $ip    = $_SERVER['REMOTE_ADDR'] ?? '';
    $error = \Local\Fraud\IpOrderLimiter::check($ip);

    if ($error) {
        return new \Bitrix\Main\EventResult(
            \Bitrix\Main\EventResult::ERROR,
            new \Bitrix\Main\Error($error)
        );
    }

    return new \Bitrix\Main\EventResult(\Bitrix\Main\EventResult::SUCCESS);
});

Ліміти на рівні nginx (перший рубіж)

nginx обробляє ліміти до PHP, не витрачаючи серверні ресурси:

# /etc/nginx/conf.d/order-limit.conf

# Зона для сторінки оформлення замовлення
limit_req_zone $binary_remote_addr zone=checkout:10m rate=2r/m;

# Зона для AJAX-запитів створення замовлення
limit_req_zone $binary_remote_addr zone=order_ajax:10m rate=5r/m;

server {
    # ...

    location = /order/ {
        limit_req zone=checkout burst=3 nodelay;
        limit_req_status 429;
        # ...
    }

    location ~ ^/local/ajax/(order|checkout) {
        limit_req zone=order_ajax burst=5 nodelay;
        limit_req_status 429;
        add_header Retry-After 60;
        # ...
    }
}

Білий список IP

Легітимні партнери або внутрішні IP не повинні потрапляти під ліміти:

private static function isWhitelisted(string $ip): bool
{
    $whitelist = [
        '127.0.0.1',
        '::1',
        '10.0.0.0/8',     // внутрішня мережа
        '192.168.0.0/16',
    ];

    foreach ($whitelist as $cidr) {
        if (str_contains($cidr, '/')) {
            if (self::ipInCidr($ip, $cidr)) return true;
        } elseif ($ip === $cidr) {
            return true;
        }
    }

    return false;
}

private static function ipInCidr(string $ip, string $cidr): bool
{
    [$subnet, $mask] = explode('/', $cidr);
    return (ip2long($ip) & ~((1 << (32 - (int)$mask)) - 1)) === ip2long($subnet);
}

Логування та моніторинг

Кожне спрацювання ліміту логується:

\Bitrix\Main\Diag\Debug::writeToFile(
    [
        'ip'      => $ip,
        'limit'   => "{$count}/{$maxOrders} за {$minutes} хв",
        'ua'      => $_SERVER['HTTP_USER_AGENT'] ?? '',
        'referer' => $_SERVER['HTTP_REFERER'] ?? '',
    ],
    'IP limit triggered',
    '/local/logs/ip-limits.log'
);

Агент раз на день парсить лог і надсилає звіт: топ-10 IP за блокуваннями, динаміка за тиждень.

Налаштування лімітів

Сценарій магазину Рекомендовані ліміти
Стандартний B2C 3/15хв, 5/год, 15/доба
B2B із великими замовленнями 5/15хв, 15/год, 50/доба
Розпродаж (тимчасово) 10/15хв, 30/год

Ліміти зберігаються в конфігу або опціях модуля — без деплою змінюються через адміністративну частину.