Все системы работают штатно 6 офшорных регионов Оформление без KYC
Hands-on Полевое руководство

DDoS-атака на ваш VPS: runbook первого часа

Первые десять минут решают, какими будут следующие три часа, а большинство в это время просто гадает — перезапускает сервисы, банит адреса наугад, смотрит на дашборд, который сообщает только одно: сайт лежит. Вот последовательность, которую мы на самом деле применяем на своём парке: убедиться, что атака настоящая, измерить её форму, снять ту нагрузку, которую можно снять изнутри самой машины, и распознать точный момент, когда то, что вы вводите на сервере, уже ничего не решает и имеет значение только 1.5 Tbps апстрим-фильтрации перед ним. Написано для одного VPS от $8.00/мес, а не для NOC.

Обновлено 2026-09-06 · 15 мин чтения · Операции с парком серверов
На этой странице
  1. Сначала убедитесь, что это действительно атака
  2. Пятиминутный триаж
  3. Что нельзя исправить изнутри самой машины
  4. L7: флуд, который выглядит как обычный трафик
  5. Четыре меры, в порядке эффекта на минуту
  6. Не боритесь с этим на машине, где лежат ваши данные
  7. День, когда отражатель — это вы, а не цель
  8. Когда всё прекратилось: post-mortem и дежурный комплект
  9. Шаг за шагом
SP·01

Сначала убедитесь, что это действительно атака

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

Три вопроса позволяют различить их меньше чем за минуту. Объём трафика действительно аномален, или трафик нормальный, а тормозит сам сервер? Флуд проявляется как резкий скачок пакетов или запросов в секунду; неудачный деплой — как обычный объём запросов при рухнувшем времени отклика. Менялось ли что-нибудь на вашей стороне за последний час? Проверьте журнал деплоев раньше, чем firewall, — на небольшой инфраструктуре инциденты, устроенные собственными руками, встречаются куда чаще настоящих атак. Нагрузка размазана по всему сайту или сосредоточена на одном пути? Настоящие флуды обычно бьют без разбора или метят в главную страницу; если дорогой эндпоинт долбят сто клиентов, это ближе к злоупотреблению, чем к DDoS, — и чинится это куда дешевле.

Ответьте на эти вопросы, а затем двигайтесь дальше. Весь оставшийся текст руководства исходит из того, что ответ был таким: объём аномален, на вашей стороне ничего не менялось, и машина тонет.

SP·02

Пятиминутный триаж

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

Отличить одно от другого можно, сравнив две цифры. Смотрите одновременно на счётчики интерфейса и на распределение нагрузки CPU. Если байты rx упёрлись в скорость порта, если счётчики dropped или overrun растут, а время уходит на программные прерывания, а не на ваше приложение, — флуд идёт на уровне L3/L4, и это проблема пропускной способности. Если с полосой всё в порядке, а пул воркеров исчерпан, соединения выстраиваются в очередь, а лог доступа полон правдоподобных на вид запросов, — это уровень L7, и это проблема фильтрации.

Затем классифицируйте случай L3/L4 ещё на шаг точнее, потому что подтипы ведут себя по-разному. SYN-флуд проявляется как десятки тысяч полуоткрытых сокетов в состоянии SYN-RECV; ядро неплохо справляется с этим само, если включены syncookies. UDP-флуд или флуд с усилением проявляется как огромный входящий объём на портах, которые вы даже не слушаете, — отражённые DNS-, NTP-, memcached- и CLDAP-ответы, — и тут не поможет ничего из того, что запущено у вас, потому что ущерб уже нанесён к моменту, когда пакеты доходят до вашей сетевой карты. Фрагментационный флуд или флуд из сырых пакетов проявляется как высокий pps при скромной полосе, что истощает не канал, а CPU. Запишите, с каким именно случаем вы столкнулись, прежде чем лезть в конфиг.

SP·03

Что нельзя исправить изнутри самой машины

Это та часть, которую пропускает большинство статей, и именно она решает, будет ли ваш час продуктивным. Правило firewall на цели не спасает захлебнувшийся аплинк. Ваш DROP в iptables выполняется на машине в конце канала — пакет уже пересёк транзитную линию, уже израсходовал полосу, за которую вы платите, и уже вытеснил пакет настоящего пользователя. Отбрасывая его локально, вы защищаете приложение от лишней траты циклов CPU — это чего-то стоит, — но вашу полосу это не защищает вообще никак.

Честный потолок для одного сервера — это примерно меньшее из двух чисел: скорость порта, к которому он подключён, и число пакетов в секунду, которое способен разобрать его CPU. Порт на 1 Gbps заполняется на 1 Gbps независимо от того, насколько элегантен ваш набор правил, а скромный флуд мелких пакетов способен занять пару ядер обработкой прерываний задолго до того, как цифра по полосе покажется тревожной. За этой точкой помогает только одно — устройство выше по цепочке, у которого мощности больше, чем у атаки, и которое сбрасывает трафик прежде, чем тот вообще доберётся до вашего канала. Именно это и называется фильтрацией (scrubbing), и именно поэтому каждая машина в нашем парке стоит за 1.5 Tbps постоянно активной защиты, а не за более крупным firewall'ом.

Следствие из этого не менее важно: если у вас нет апстрим-защиты, ответ провайдера на крупную волюметрическую атаку — отправить ваш адрес в null-route, потому что альтернатива — деградация всех остальных клиентов на этом канале. Это не злой умысел, а арифметика, — и получается, что атакующий побеждает не тем, что что-то ломает, а тем, что делает вас слишком дорогим. Заранее выяснить, включена ли защита от атак в ваш тариф или это платная опция, которую вы так и не подключили, — проверка на пять минут, и сделать её стоит сегодня, а не во время инцидента. У нас это включено в каждый тариф, и это единственная схема, которая помогает в три часа ночи.

SP·04

L7: флуд, который выглядит как обычный трафик

Флуд на уровне приложения сложнее, потому что каждый отдельный запрос легитимен. Хорошо сделанный HTTP-флуд полностью проходит TCP-рукопожатие, договаривается о TLS, отправляет корректный GET / с правдоподобным user agent'ом и читает ответ. Ни один пакет в нём не деформирован. Убивает вас арифметика: атакующему запрос почти ничего не стоит отправить, а вам он обходится в запрос к базе, рендер шаблона и сотню миллисекунд воркера, который в это время не обслуживает никого другого.

Признаки — в вашем же логе доступа, и они обычно очевидны, если их искать, а не просто смотреть на них. Query-строки, обходящие кэш — тысячи запросов к /?1234567, каждый на уникальный URL, который сводит на нет любой ваш кэш, — самая частая примета из всех. Распределение user agent'ов без длинного хвоста: настоящий трафик — это неопрятная смесь из сотен сборок браузеров, а флуд — это часто три строки, повторённые миллион раз, либо строка, которой не пользуется ни один настоящий пользователь. Поле referrer, одинаковое везде. Запросы, полностью игнорирующие ваши статические ресурсы — настоящий браузер после HTML подтягивает CSS, шрифты и изображения; клиент-флудер запрашивает HTML и уходит. И слишком плоское распределение источников: ботнет, размазанный по десяти тысячам домашних адресов, каждый из которых шлёт по два запроса в секунду, выглядит как популярность — пока вы не заметите, что частота на адрес подозрительно однородна.

Затем есть вариант, которому почти не нужен трафик вообще, — медленная атака. Несколько сотен соединений, которые открываются, шлют по одному заголовку каждые двадцать секунд и никогда не завершаются, займут всех ваших воркеров, пока график полосы остаётся плоским. Лечится это не rate limit'ом — частота запросов здесь мизерная, — а агрессивными таймаутами на заголовки и тело запроса, поэтому они и стоят в первом же блоке конфигурации ниже, а не идут довеском в конце.

SP·05

Четыре меры, в порядке эффекта на минуту

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

  • Отдавайте что-то дешёвое. Микрокэш на тридцать секунд перед вашим приложением превращает тысячу одинаковых запросов в секунду в одно обращение к origin и 999 чтений из памяти. Это самый мощный рычаг почти против любого HTTP-флуда, стоит он один блок директив, а для анонимного трафика это почти всегда безопасно. Добавьте proxy_cache_lock, чтобы промах мимо кэша не обрушивал на бэкенд лавину одинаковых запросов (thundering herd).
  • Ограничивайте число одновременных соединений и сокращайте таймауты. limit_conn на адрес плюс жёсткие таймауты на заголовки, тело и keepalive напрочь убивают медленные атаки и не дают ни одному клиенту в одиночку занять весь ваш пул воркеров. Это мера, которая обходится настоящим пользователям дешевле всего.
  • Ограничивайте частоту запросов, с запасом на всплеск. limit_req с разумным значением burst — мера точная, но настраивается дольше, и именно она даёт ложные срабатывания, если выставлять её в панике, а не по собственным базовым показателям. Прежде чем выбрать число, нужно знать свою нормальную частоту запросов на клиента в секунду — именно поэтому post-mortem в конце этого руководства важнее, чем кажется на первый взгляд.
  • Блокируйте — точечно и неохотно. Сброс конкретных сетей работает, когда источники сконцентрированы, и бесполезен, когда это не так. Кроме того, это плохо стареет: каждая блокировка, добавленная во время инцидента, — это клиент, которому вы, возможно, будете молча отказывать ещё три месяца спустя. Используйте set с таймаутом, чтобы правила истекали сами.

Обратите внимание, чего в списке нет: ручного бана отдельных IP-адресов, повторных перезапусков веб-сервера и отключения firewall, «чтобы посмотреть, поможет ли». Первое слишком медленно, чтобы иметь значение против распределённого источника, второе выбрасывает все ваши уже прогретые соединения, а третье — это именно то, как инцидент превращается во взлом.

SP·06

Не боритесь с этим на машине, где лежат ваши данные

Любая мера из перечисленных выше даёт больше пользы, если работает не на той машине, где лежит ваша база данных. Если ваш edge — это отдельный узел, флуд обрывается на машине, единственная задача которой — обрывать флуды: она кэширует, ограничивает частоту и отбрасывает трафик, имея на руках настоящий адрес клиента, а origin вообще видит только небольшой, уже отфильтрованный остаток — и то через приватный туннель. Если edge падает, вы заменяете его за 15 min и ничего не теряете, потому что на нём и не было ничего. Если падает origin, у вас простой и восстановление из бэкапа.

Это же разделение закрывает и обход, из-за которого большая часть защиты от атак оказывается чисто декоративной. Если у origin всё ещё есть публичный слушатель, атакующий, нашедший его адрес — через пассивный DNS, запись в журнале прозрачности сертификатов, MX-запись или предпросмотр ссылки, — может обойти все настроенные вами меры и ударить прямо по приложению. Это встречается достаточно часто, чтобы считать это состоянием по умолчанию для любого «защищённого» сайта, пока не доказано обратное. Версия, которая держится, — это отдельное руководство: офшорный reverse-proxy, у которого на origin вообще нет ни одного публичного слушателя.

Одно предупреждение насчёт середины инцидента: это архитектура, а не первая помощь. Разворачивать edge, переносить DNS и пересобирать туннель прямо под атакой — это работа на два часа, которую под давлением делают плохо, а одно только изменение DNS не вступит в силу раньше, чем истечёт срок, заданный вашим TTL. Если это у вас уже есть — используйте. Если нет — переживите этот час теми мерами, что есть под рукой, а разворачивайте это в спокойную неделю после инцидента, — именно тогда этого никто и не делает.

SP·07

День, когда отражатель — это вы, а не цель

У этого инцидента есть и вторая версия, где ваш сервер — не жертва, а вам об этом никто не сообщает. Открытый резолвер, незащищённый демон NTP, memcached без аутентификации на публичном интерфейсе, SSDP- или CLDAP-сервис внутри контейнера, отвечающий на запросы, — каждый из них отвечает на небольшой запрос с подделанным обратным адресом гораздо более объёмным ответом, нацеленным на кого-то другого. С вашей стороны симптомы перевёрнуты: исходящий трафик высокий, входящий скромный, приложение чувствует себя прекрасно, а первым реальным сигналом становится уведомление о злоупотреблении или отключённый порт.

Проверка занимает минуту и относится к тому же runbook'у, потому что это та же самая команда, которую вы уже запускали во время триажа. ss -tulpn не должна показывать ничего, привязанного к публичному адресу, что вы не помещали туда намеренно, а UDP-сервисы заслуживают особого подозрения, потому что именно они дают усиление. Рекурсивный резолвер обязан быть привязан к localhost или к адресу туннеля; memcached и Redis не должны быть доступны из интернета вообще никогда; а любой контейнер, публикующий порт через -p 0.0.0.0:, только что пробил дыру в firewall, который вы настраивали, — потому что Docker прописывает собственные правила раньше ваших. Вот это каждый раз оказывается сюрпризом.

Та же картина накрывает и исходящие флуды с уже скомпрометированной машины — это ещё одна причина, по которой провайдер вдруг отправляет адрес в null-route. Если график исходящего трафика высокий, а приложение простаивает, перестаньте читать конфиги и начните проверять процессы — это вторжение, а не проблема пропускной способности, и правильный ответ — пересобрать машину из заведомо чистого бэкапа, а не пытаться её отфильтровать.

SP·08

Когда всё прекратилось: post-mortem и дежурный комплект

Атаки заканчиваются. Обычно атакующему становится скучно, иногда защита делает атаку бессмысленной, а иногда это была подписка на booter с фиксированным сроком, которая просто истекла. Соблазн в этот момент — оставить всё как есть и лечь спать, и именно так временный rate limit превращается в постоянный, забытый, молчаливый 429 для целой страны девять месяцев спустя. Потратьте двадцать минут, чтобы закрыть цикл, пока всё ещё свежо в памяти.

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

Затем закройте два структурных пробела, потому что именно здесь этот слой встаёт в общую схему. Защита поглощает объём, edge не подпускает флуд к вашим данным, а машина позади них всё равно должна быть как следует защищена и иметь восстанавливаемые бэкапы вне хоста, потому что следующий инцидент вполне может оказаться вовсе не флудом. Сервер, который пережил DDoS, а через месяц потерял диск, никогда не был устойчивым — ему просто повезло дважды.

SP·09

Шаг за шагом

  1. 01

    Подтвердите, прежде чем что-то менять

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

    # is the box alive, and where is the time going?
    uptime                     # load average against your core count
    vmstat 1 5                 # 'in' and 'cs' high, 'id' near zero = packet work
    mpstat -P ALL 1 3          # %soft pinned on one core = interrupt saturation
    
    # is the pipe full, or just busy?
    ip -s link show eth0       # rx bytes, and the errors/dropped counters
    ethtool eth0 | grep -i speed
    
    # requests per second from your own log, minute by minute
    tail -n 20000 /var/log/nginx/access.log \
      | awk -F'[][]' '{print $2}' | cut -d: -f2-4 | uniq -c | tail -5

    Прежде чем делать вывод, что это атака, проверьте собственный журнал деплоев и таблицу cron. Инциденты, вызванные собственными руками, на одном VPS случаются чаще, чем флуды, а выглядят снаружи точно так же.

  2. 02

    Измерьте форму трафика за шестьдесят секунд

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

    # top source addresses on the wire right now
    timeout 20 tcpdump -nn -i eth0 -c 20000 2>/dev/null \
      | awk '{print $3}' | rev | cut -d. -f2- | rev \
      | sort | uniq -c | sort -rn | head -20
    
    # TCP state census: a wall of SYN-RECV is a SYN flood
    ss -tan | awk '{print $1}' | sort | uniq -c | sort -rn
    
    # layer 7: talkers, paths, agents over the last 50k requests
    L=/var/log/nginx/access.log
    tail -n 50000 $L | awk '{print $1}' | sort | uniq -c | sort -rn | head -20
    tail -n 50000 $L | awk '{print $7}' | sort | uniq -c | sort -rn | head -20
    tail -n 50000 $L | cut -d'"' -f6   | sort | uniq -c | sort -rn | head -10

    Сверьте результат с приметами: уникальные query-строки на одном пути, список user agent'ов без длинного хвоста, отсутствие запросов к статике, или частота на адрес, подозрительно однородная. Если полоса высокая на портах, которые вы не слушаете, — остановитесь здесь: это волюметрический флуд, и значение имеет только шаг шестой.

  3. 03

    Отдавайте что-то дешёвое и ограничьте соединения

    Сначала — то, что даёт наибольший эффект. Микрокэш на тридцать секунд схлопывает флуд одинаковых анонимных запросов в одно обращение к origin, а жёсткие таймауты убивают медленные атаки, которые rate limit просто не видит. Поместите и то, и другое в блок http, а затем сделайте reload, а не restart, чтобы сохранить прогретые соединения.

    # /etc/nginx/nginx.conf — http block
    limit_conn_zone $binary_remote_addr zone=perip:10m;
    limit_req_zone  $binary_remote_addr zone=flood:20m rate=10r/s;
    
    client_header_timeout 10s;
    client_body_timeout   10s;
    send_timeout          10s;
    keepalive_timeout     20s;
    reset_timedout_connection on;
    
    proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=hot:64m
                     max_size=2g inactive=10m use_temp_path=off;
    # the server block — cap concurrency, serve the cached copy
    limit_conn perip 20;
    
    location / {
        proxy_cache hot;
        proxy_cache_valid 200 301 302 30s;
        proxy_cache_lock on;
        proxy_cache_use_stale error timeout updating
                              http_500 http_502 http_503 http_504;
        add_header X-Cache $upstream_cache_status;
        proxy_pass http://127.0.0.1:8080;
    }
    nginx -t && systemctl reload nginx
    curl -sI https://example.com/ | grep -i x-cache   # want: HIT on the second call
  4. 04

    Ограничивайте настоящего клиента, а не собственный прокси

    Если перед nginx что-то стоит, каждый запрос приходит с одного и того же адреса, и лимит на адрес будет душить прокси вместо атакующего — или вовсе забанит его и положит сайт как раз тогда, когда вы его защищаете. Доверяйте заголовку с прокинутым адресом, но только от адреса прокси, и никогда — от всего интернета, а уже потом применяйте лимит.

    # /etc/nginx/conf.d/realip.conf — the tunnel or edge address only
    set_real_ip_from 10.66.0.1;
    real_ip_header   X-Forwarded-For;
    real_ip_recursive off;
    # burst absorbs bursty humans; nodelay keeps the page fast for them
    location / {
        limit_req zone=flood burst=20 nodelay;
        limit_req_status 429;
    }
    
    # the expensive paths get a much tighter bucket of their own
    location ~ ^/(search|login|register|api/) {
        limit_req zone=flood burst=5;
        limit_req_status 429;
    }

    Затем посмотрите, что вы только что сделали: tail -f /var/log/nginx/error.log | grep limiting. Если ограничиваемые адреса похожи на ваших клиентов, значит, порог слишком низкий, — поднимите его. Лимит, который блокирует настоящих пользователей, — это простой, который вы устроили себе сами.

  5. 05

    Блокируйте точечно и давайте каждой блокировке срок действия

    Имеет смысл, только если шаг два показал сконцентрированные источники. Используйте set, а не тысячу правил — поиск в ipset выполняется за константное время, а длинная цепочка iptables обходится для каждого пакета и сама превращается в своего рода отказ в обслуживании. Давайте каждой записи таймаут, чтобы сегодняшняя чрезвычайная мера не стала в следующем году молчаливым списком блокировки.

    ipset create flood hash:net timeout 3600 -exist
    iptables -I INPUT -m set --match-set flood src -j DROP
    
    # feed it from the census: /24s you actually verified, not guesses
    for n in 203.0.113.0/24 198.51.100.0/24; do ipset add flood $n -exist; done
    
    # SYN flood: let the kernel do the part it is good at
    sysctl -w net.ipv4.tcp_syncookies=1
    sysctl -w net.ipv4.tcp_max_syn_backlog=8192
    sysctl -w net.core.somaxconn=8192
    
    ipset list flood | head -20      # keep a copy of this for the post-mortem

    Не поддавайтесь соблазну заблокировать по гео сразу целую страну, если не можете назвать поимённо клиентов, которых отрезаете. И никогда не используйте ufw disable, чтобы проверить теорию: машина без firewall под активной атакой — это способ превратить инцидент с полосой пропускания во взлом.

  6. 06

    Эскалируйте на уровень, который действительно способен это поглотить

    Если счётчики интерфейса говорят, что канал забит, или дропы растут при простаивающем CPU, вы упёрлись в потолок того, что вообще можно сделать на сервере. Подтвердите эти показания, а затем эскалируйте, а не продолжайте тюнинг.

    # drops in the stack itself — second column is 'dropped'
    awk '{print strtonum("0x" $2)}' /proc/net/softnet_stat | paste -sd+ | bc
    
    # interface-level loss against link speed
    ip -s link show eth0 | sed -n '3,6p'

    При включённой апстрим-фильтрации обычно делать нечего: детектирование работает постоянно, и флуд сбрасывается в сети раньше, чем доходит до вашего порта, — на нашем парке это 1.5 Tbps мощности перед каждым тарифом, так что инцидент зачастую виден постфактум только как график. Если атака — это корректно сформированный HTTP-флуд, а не волюметрический, то это уже случай для L7-защиты, потому что флуд запросов неотличим от пользователей на уровне пакетов и должен оцениваться выше по стеку. Если у вас нет вообще никакой защиты, реалистичные варианты — либо встать за edge, у которого она есть, либо ждать, — и спланировать первое стоит до, а не после следующей атаки.

  7. 07

    Закройте цикл: проверьте, откатите, затем запишите

    Проверяйте снаружи машины, а не из shell'а на ней самой. Затем осознанно отмените аварийные меры, оставив те, что были хорошей идеей в любом случае, и зафиксируйте цифры, чтобы следующий инцидент отталкивался от базовых показателей, а не от догадки.

    # from somewhere else entirely: is the site healthy for a normal user?
    curl -s -o /dev/null -w 'code=%{http_code} ttfb=%{time_starttransfer}s\n' \
         https://example.com/
    
    # did you leave a limit that is biting real people?
    grep -c 'limiting requests' /var/log/nginx/error.log
    
    # what is still blocked, and when does it expire?
    ipset list flood | head -30

    Оставьте кэш, таймауты и syncookies — это постоянные улучшения. Откатите агрессивные rate limit'ы к измеренным базовым показателям с запасом и дайте записям в ipset истечь самим. Затем впишите четыре команды из первого и второго шагов в runbook, хранящийся где-то не на этом сервере, — рядом с вашей нормальной частотой запросов в секунду и нормальной пиковой полосой. Следующая атака станет более коротким инцидентом просто потому, что эти две цифры уже есть.

SP·10 — FAQ

Быстрые ответы

Как отличить настоящий DDoS от скачка трафика?

Смотрите на форму, а не на размер. У органических всплесков есть структура: они идут из множества сетей, user agent'ы образуют неопрятный длинный хвост, клиенты подтягивают CSS и изображения после HTML, а referrer обычно указывает на что-то реальное. Флуд же плоский и однообразный — одна и та же горстка агентов, никаких запросов к статике, одинаковая частота на клиента и часто уникальные query-строки, придуманные специально, чтобы обойти кэш. Ещё две вещи отсеиваются быстро: прежде чем кого-то обвинять, проверьте журнал деплоев и таблицу cron, потому что медленный запрос, выкаченный час назад, даёт те же симптомы и требует противоположного лечения.

Могут ли iptables или fail2ban остановить DDoS?

Они помогают против небольших и сконцентрированных атак и бесполезны против волюметрических. Оба работают на машине в конце канала, так что к моменту срабатывания правила пакет уже израсходовал вашу полосу — вы экономите циклы CPU, а не пропускную способность канала. fail2ban к тому же реактивен по своей природе: он читает лог постфактум, а для флуда это опоздание на целые минуты, и он вполне может забанить ваш собственный reverse-proxy, если вы не настроили получение настоящего адреса клиента. Используйте их как гигиену против перебора паролей и сканеров; против объёма используйте апстрим-фильтрацию.

Поможет ли смена IP-адреса сервера?

Ненадолго и редко, а стоит это вам каждого DNS-кэша в мире. Если атакующий целится в hostname, новый адрес появляется в DNS за считанные минуты, и флуд следует за ним; смена адреса даёт что-то, только если целью был голый адрес, а у атакующего нет простого способа зарезолвить его заново. По-настоящему работающая версия — структурная, а не косметическая: поставьте впереди заменяемый edge, держите origin недоступным из публичного интернета, и тогда адрес, который атакуют, — это адрес, который можно выбросить за 15 min, не трогая ваши данные.

Добавляет ли защита от DDoS задержку и не ломает ли она что-нибудь?

Фильтрация на L3/L4 работает постоянно и практически незаметна — пакеты проходят через фильтрующий путь, а не перенаправляются только на время события, так что нет ни момента переключения, ни ощутимых потерь на обычном трафике. С фильтрацией на уровне приложения всё иначе, потому что решить, человек перед вами или нет, иногда означает бросить ему challenge, а любой challenge задевает небольшое число нетипичных клиентов. Два сценария сбоя, которые стоит продумать заранее, — это закэшированные ответы, отданные залогиненным пользователям (это проблема ключа кэша, а не защиты), и API-клиенты, не способные пройти challenge, — именно поэтому пути вашего API стоит осознанно исключать заранее, а не узнавать об этом на практике.

Может ли мой сервер сам атаковать кого-то другого?

Такое случается чаще, чем принято думать, и симптомы при этом перевёрнуты: высокий исходящий трафик, нормальный входящий, приложение чувствует себя прекрасно, а первым реальным сигналом становится уведомление о злоупотреблении. Обычные причины — это открытый DNS-резолвер, незащищённый сервис NTP или memcached, либо контейнер, опубликовавший порт на 0.0.0.0 и прописавший собственное правило firewall раньше вашего. Запустите ss -tulpn и убедитесь, что ничего непреднамеренного не привязано к публичному адресу. Если исходящий трафик высокий, а никакой легитимный сервис этого не объясняет, считайте это взломом и пересобирайте машину из заведомо чистого бэкапа, а не пытайтесь отфильтроваться.

Защита L3/L4 уже включена — нужна ли мне ещё и L7-защита?

Это полностью зависит от того, что вы держите. Статический сайт, игровой сервер или что угодно, где нагрузка измеряется в пакетах, а не в рендерах страниц, отлично прикрывается одной лишь фильтрацией на сетевом уровне. Динамическое приложение с дорогими эндпоинтами — поиск, логин, оформление заказа, API, обращающийся к базе, — можно положить парой тысяч совершенно валидных запросов в секунду, к которым ни один пакетный фильтр никогда не придерётся, и именно для этого нужна защита на уровне приложения. Но самый дешёвый первый шаг — не она: это микрокэш и rate limit по путям из третьего и четвёртого шагов, которые бесплатно снимают большую часть арифметического преимущества атакующего.

Применить на практике

VPS в онлайне за 15 min, выделенный передаётся за 2–12 h. Пополните от $30.00 в крипте — без привязки личности.

Развернуть VPS