Перейти к содержимому

Почему сама блокировка становится нагрузкой

«Мы блокируем атаку» — недостаточная фраза. Вопрос другой: на каком уровне выполняется блокировка и во что она вам обходится?

DDoS
/
2026-07-22
/
7 мин чтения

Отклонённый запрос тоже не бесплатен

Запрос, заблокированный на уровне приложения, проходит весь путь: принимается TCP-соединение, выполняется рукопожатие TLS, читаются заголовки HTTP, срабатывает правило и пишется ответ 403.

То есть на каждый запрос, который вы видите как «заблокирован», сервер потратил соединение, шифрование и ответ. Для одного запроса это ничтожно. Для ста тысяч в секунду это и есть причина падения.

Самая дорогая часть — TLS. При рукопожатии с ключом RSA-2048 операция подписи на сервере в разы дороже, чем работа клиента: эта асимметрия не злой умысел, а природа протокола. ECDSA P-256 заметно дешевле, но всё равно на порядки дороже простого приёма пакета.

Атакующий это знает. Поэтому часть самых эффективных L7-атак вообще не отправляет запрос: соединение открывается, TLS начинается и на этом останавливается. С вашей стороны это даже не «заблокированный запрос» — в статистику он не попадает, а память и файловый дескриптор занимает.

Поэтому график вводит в заблуждение

Во время атаки на панели видно «заблокировано запросов: 2,4 млн», и кажется, что защита работает. А сайт при этом не открывается.

Причина простая: считается статистика блокировок, а каждая блокировка означает выполненную работу. Чем больше число, тем больше сервер поработал. Рост графика — это рост расхода, а не защиты.

Правильная панель показывает на одном графике две линии: заблокированные запросы и загрузку процессора в тот же момент. Полезна и третья — число открытых соединений, потому что во многих случаях первым заканчивается не процессор, а именно оно.

Экзотических инструментов для этого не нужно. `ss -s` показывает открытые сокеты одной строкой, `netstat -s` даёт счётчики повторных передач и отброшенных пакетов. Если во время атаки растут `TCPBacklogDrop` или `ListenOverflows`, вы уже упёрлись в очередь, и правило блокировки на это никак не влияет.

Схема 1Один и тот же запрос, две цены. По верхнему пути до ответа 403 тратятся соединение, TLS и ответ; по нижнему пакет вообще не доходит до приложения.

Что меняет отсечение на уровне ядра

После того как источник определён, решение не должно подниматься наверх. Пакет отбрасывается на самой нижней ступени сетевого стека — до приложения он не доходит, TLS не открывается, ответ не пишется.

На практике уровней несколько. Самый нижний — сама сетевая карта или XDP: пакет отбрасывается на уровне драйвера, без создания сокета и без `sk_buff`. Выше — nftables или iptables, то есть уже после `conntrack` и маршрутизации. Самый верхний — правило внутри приложения.

Разница не теоретическая. Каждая ступень требует выделения дополнительных структур на пакет, блокировок и переключений контекста; для пакета, отброшенного в XDP, ничего из этого не выполняется. На практике это означает на порядок больше пакетов на том же железе.

Второй выигрыш незаметен, но важнее: отброшенный пакет не занимает места в таблице соединений. Когда переполняется `nf_conntrack`, новые соединения начинают отклоняться, и это касается всего сервера — и атакуемого сайта, и соседнего. Падение чаще всего начинается именно отсюда.

Почему нельзя опустить в ядро всё

Ядро видит пакет, а не сессию. Оно не сделает вывод «этот пользователь восемнадцать раз добавил один и тот же товар в корзину» — для такого вывода нужен контекст, а контекст живёт наверху.

Программа XDP вообще работает в жёстко ограниченной среде: верификатор eBPF не разрешает циклы, динамическую память и долгое исполнение. Это не недостаток, а условие — именно благодаря этим ограничениям она успевает работать на скорости пакета.

Поэтому правильная архитектура двухуровневая. Решение принимается наверху, в контексте поведения; исполнение выполняется внизу, там, где дёшево. Обнаружение должно быть умным, отсечение — грубым и быстрым.

Связывает их список: логика наверху определяет источник и передаёт его вниз со сроком. Технически это обычно eBPF map или `ipset`, потому что запись туда не требует перезагрузки набора правил. Срок обязателен — вечный чёрный список через год включает и настоящих пользователей.

Сколько должна жить запись в чёрном списке

Срок блокировки источника задаёт две разные ошибки. Слишком короткий — атакующий возвращается снова и снова, а логика обнаружения работает непрерывно. Слишком длинный — тысяча пользователей за NAT мобильного оператора часами не может зайти.

Практическое решение — растущий срок: короткий при первом срабатывании, длиннее при повторе. Случайно попавший пользователь выходит быстро, а возвращающийся источник отодвигается всё дальше.

В IPv6 правило другое. Одному хосту обычно выделяется /64, то есть блокировка одного адреса не даёт ничего — атакующий переходит на соседний. Поэтому в IPv6 блок применяется по /64, а иногда и по /48, и это решение должно быть записано отдельно.

И каждая запись должна хранить причину: какое правило, в какое время, по какому признаку. Список без причин через месяц превращается в чёрный ящик, к которому никто не решается притронуться: на вопрос «почему этот IP здесь?» ответа нет, а удалять страшно.

Схема 2Обнаружению нужен контекст, исполнению — скорость. Собрать оба на одном уровне значит испортить одно из двух.

Атаки L3/L4 и L7 — не одно и то же

Объёмная атака забивает канал, и поглощается она на уровне оператора связи. Речь здесь не о процессоре, а о ширине канала — такую атаку невозможно остановить рядом с сервером: канал уже забит, а ваше правило стоит на другом его конце.

Классические примеры — усиление через DNS или NTP и SYN-флуд. Ответ на них тоже другой: центр очистки на аплинке, увод трафика по BGP или `SYN cookies` (в Linux `net.ipv4.tcp_syncookies`), позволяющие отвечать без хранения состояния при переполнении очереди.

Атака на уровне приложения выглядит иначе: объём трафика небольшой, запросы корректные, TLS настоящий, User-Agent убедительный. Но каждый из них просит дорогую страницу — поиск, отчёт, каталог с фильтрами. Здесь падает не канал, а база данных, и сетевой график при этом выглядит совершенно спокойным.

Одной меры на оба случая не бывает. Фраза «у нас есть защита от DDoS» бессмысленна, пока не сказано, о каком из двух идёт речь, — а во многих предложениях подразумевается только первый.

Временный отказ — тоже инструмент

Блокировать каждый подозрительный запрос необязательно. Иногда дешевле замедлить его или поставить проверку, требующую вычислений: настоящий браузер этого не заметит, а скрипт, шлющий тысячи запросов, потеряет скорость.

Это особенно полезно там, где уверенности нет. Если ошиблась блокировка, пользователь теряется целиком; если ошиблось замедление, он ждёт несколько сотен миллисекунд. Цена ошибки падает — а значит, порог можно поставить строже.

На уровне HTTP это делается через 429 и заголовок `Retry-After` — механизм стандартный, и хорошо написанные клиенты ему подчиняются. Плохо написанные не подчиняются, и именно эта разница становится полезным сигналом: клиент, игнорирующий `Retry-After`, не браузер.

Важное условие: замедление тоже должно выполняться там, где дёшево. Держать соединение открытым и тянуть с ответом — значит занимать собственные дескрипторы. Ожидание должно быть без состояния: ответить сразу и отклонить следующий запрос дешевле, чем удерживать открытое соединение.

Схема 3Во время атаки число заблокированных запросов и загрузка процессора должны стоять на одном графике. Если они растут вместе, блокировка выполняется на уровне приложения.

Как это проверить

Спросите у поставщика одно: меняется ли загрузка процессора сервера для заблокированного источника? Если вместе с числом «заблокированных запросов» растёт и процессор — блокировка выполняется на уровне приложения.

Второй вопрос: выполняется ли рукопожатие TLS для запроса с заблокированного адреса? Это легко проверить — запустите `openssl s_client -connect host:443` с заблокированного адреса. Если сертификат вернулся, самую дорогую часть вы всё равно оплачиваете, а блокировка лишь меняет текст ответа.

Третий вопрос про срок: сколько живёт запись в чёрном списке и что его продлевает? Если ответа нет, значит список чистят вручную — то есть не чистят.

Четвёртый раскрывает больше всех: где считаются отброшенные пакеты во время нагрузочного теста? Если поставщик вообще не может показать это число, значит ниже прикладного уровня отбрасывания не происходит.

Все материалы

Познакомимся?

Покажите домен — дадим первичный анализ вашего текущего внешнего состояния. На этом этапе ничего не меняется.

Начнём
Связаться
Связаться