Коротке оглавлення:
— DDoS без міфів: що ламають і чому “замок” не рятує
— Наш базовий захист на мережі: що він робить і що відсікає
— Чому L7 — найпідступніший рівень і як його розпізнати
— Cloudflare як щит: налаштування, які реально працюють
— Nginx і бекенд: як пережити HTTP-флуд і не вбити UX
— Практичний план дій під час атаки
DDoS без міфів: що насправді відбувається
Коли сайт “падає”, майже завжди є одна причина: десь закінчилися ресурси. Це може бути канал, може бути мережевий стек, може бути пул воркерів у PHP, може бути база даних, яка від великої кількості запитів починає задихатися. Саме тому DDoS сьогодні — це не лише про “мільйони пакетів”, а й про дуже хитрі атаки, які виглядають як звичайні користувачі, але діють масово і методично.
Зазвичай атаки ділять на два великі типи. Перший — мережевий (L3/L4): тут або забивають канал, або б’ють по транспортному рівню (SYN, UDP, amplification), змушуючи сервер витрачати сили на обробку сміття. Другий — прикладний (L7): тут атакувальник робить вигляд, що він “клієнт”, тільки цей “клієнт” заходить на логін 10 000 разів на хвилину, викликає пошук, фільтри, API, і кожен запит запускає важкий код чи запити до БД.
Тому правильний захист завжди шаруватий. Немає одного “чарівного” перемикача. Є базова мережна фільтрація, є периметр на рівні CDN/WAF, і є грамотні налаштування веб-сервера та застосунку.
Який Anti-DDoS ми даємо клієнтам “з коробки”
У нашій інфраструктурі базовий захист працює на мережному рівні датацентра. Для клієнта це виглядає просто: ти запускаєш VPS або виділений сервер — і вже отримуєш мережний шар, який відстежує аномалії та автоматично відсікає типові DDoS-вектори ще до того, як вони “приляжуть” на твою машину.
Чому це важливо? Бо якщо почати відбивати volumetric-атаку “всередині” сервера, ти програєш ще до того, як відкриєш лог. Канал може забитися раніше, ніж nginx встигне сказати “404”. Саме тому мережний захист має бути не на рівні “давай поставимо iptables”, а на рівні “очистимо трафік до того, як він дійде”.
Як це працює в реальному житті: система бачить, що трафік став підозрілим — наприклад, різко виросла кількість пакетів на секунду, або з’явився нетиповий профіль SYN/ACK, або пішов лавиноподібний UDP по конкретних портах. Далі вмикаються фільтри, які прибирають сміття, а нормальний трафік пропускають. Для більшості класичних L3/L4 атак цього достатньо, щоб сайт залишався доступним.
Але чесно: мережний шар — це не панацея від усього. Він добре прибирає “шум” і “грубу силу”, але якщо атака дуже схожа на звичайний HTTPS-трафік, потрібно підключати наступні рівні.
L7-атаки: коли запити “легальні”, а сайт помирає
Найнеприємніший сценарій — коли графіки “ніби нормальні”: трафік не космічний, канал не забитий, але сайт то відкривається, то ні. В цей момент часто винна саме L7-атака. Уяви, що замість 200 людей на годину до тебе заходить 20 000 “людей” на хвилину, і кожен натискає “Пошук”, або “Увійти”, або “Додати в кошик”. Навіть якщо це по 1–2 KB на запит, для бекенду це може бути кілотонна робота: авторизація, сесії, bcrypt, SQL, зовнішні API.
У L7-атаках є дві проблеми. Перша — важко відрізнити бота від людини без поведінкових сигналів. Друга — вони майже завжди б’ють у “дорогі” маршрути. Тому захист тут — це комбінація Cloudflare, лімітів у Nginx і оптимізації застосунку.
Cloudflare: як зробити так, щоб він реально захищав, а не “просто стояв”
Cloudflare часто підключають “для SSL і кешу”, і забувають про головну річ: якщо ваш origin-сервер доступний напряму по IP, будь-який серйозний атакувальник Cloudflare обійде. Тому перше правило: origin треба сховати.
Що це означає на практиці:
-
DNS-записи сайту мають бути під проксі Cloudflare (щоб користувачі бачили IP Cloudflare, а не ваш).
-
На сервері потрібно налаштувати firewall так, щоб 80/443 приймались тільки з мереж Cloudflare. Тоді прямі запити на IP сервера не матимуть сенсу.
-
В ідеалі — окремо закрити адмінку, SSH і службові порти: доступ лише з ваших IP або через VPN.
Далі — WAF і правила. Тут важливо не зробити “ядерну зиму” для нормальних людей. Найкраще працює підхід “захищаємо больові точки”:
-
логін і все, що пов’язано з авторизацією;
-
дорогий пошук/фільтри;
-
API, особливо якщо воно відкрите в інтернет;
-
сторінки, які генерують складні відповіді (каталоги, персональні кабінети).
На ці маршрути ставиться rate limiting: не “заборонити”, а акуратно обмежити частоту. Наприклад, звичайна людина не логіниться 50 разів за хвилину. А бот — запросто. Тому правила з порогами й “бурстом” працюють дуже ефективно.
І ще один важливий інструмент — challenge/підвищена перевірка на час інциденту. Це не режим “назавжди”, бо він впливає на UX, але як аварійний важіль — часто рятує, поки ти налаштовуєш тонші правила.
Nginx та бекенд: як не згоріти від HTTP-флуду
Навіть ідеальний Cloudflare не гарантує, що до origin не долетить частина трафіку. Іноді атака йде “легально” через браузери, іноді це реальні користувачі з поганими мережами, іноді правила ще не допиляні. Тому сервер теж має бути готовий.
Є простий принцип: не давай одній адресі або одному клієнту з’їсти всі твої ресурси. У Nginx це досягається двома речами: ліміт запитів і ліміт з’єднань. Вони не повинні бути агресивними “по всьому сайту”. Натомість став їх точково на дорогі URL: логін, API, пошук, сторінки з формами.
Далі — таймаути. Під атаками часто вилітає “повільне читання” (slowloris-подібні ефекти), коли клієнти тримають конекти і висмоктують воркери. Адекватні client_header_timeout, client_body_timeout, коротший keepalive_timeout для підозрілих шляхів — дають дуже відчутний ефект.
Третій шар — кеш. Якщо сторінка або відповідь може кешуватися хоча б 10–30 секунд, це інколи рятує життя: запити перестають добігати до PHP/Node/DB і віддаються з пам’яті або диска. Для контентних сторінок це майже завжди win.
На рівні застосунку корисно мати “режим шторму”: коли ти тимчасово зменшуєш функціональність. Наприклад, вимикаєш важкі фільтри, показуєш спрощену сторінку каталогу, переносиш частину операцій у черги, вмикаєш додаткову перевірку на логін. Це неприємно, але краще, ніж повний даунтайм.
Як діяти під час атаки, щоб не метушитися
Коли атака вже йде, головне — не починати “випадково крутити все”. Краще діяти послідовно:
-
У Cloudflare швидко підняти рівень перевірок (challenge) і ввімкнути правила для найчастіших цілей (логін, API).
-
Перевірити, що origin справді закритий і не приймає прямі 80/443 з інтернету.
-
На Nginx поставити/посилити ліміти саме на ті URL, які зараз б’ють.
-
В логах подивитися патерни: країни, ASN, user-agent, конкретні URI — і під ці патерни додати правила.
-
Якщо бекенд/БД задихається — тимчасово прибрати найдорожчі функції
Як виглядають різні атаки в житті: симптоми, які легко сплутати
Є типовий момент: клієнт каже “нас DDoS-ять”, а насправді це може бути що завгодно — від повільної бази до 3rd-party API, який завис. Але є кілька ознак, які дуже характерні саме для DDoS-сценаріїв.
SYN flood часто відчувається так, ніби “сайт періодично зникає”, хоча CPU може бути ще не в червоній зоні. У мережевих метриках зазвичай росте кількість нових TCP-з’єднань або спроб з’єднання, а у воркерах з’являється відчуття “вічної черги”: нормальним користувачам банально не вистачає слотів для встановлення конекту. На рівні веб-сервера це інколи виглядає як лавина таймаутів, коли відповіді ніби є, але дуже нестабільні.
UDP flood / amplification зазвичай більш “грубий”. Якщо атака потужна, першим симптомом стає деградація мережі: зростає вхідний трафік, підвисає навіть SSH, моніторинг “не встигає”. Якщо ж мережне очищення спрацювало, то для клієнта це часто виглядає як короткий сплеск і повернення до норми. Саме тому мережний захист — базова лінія оборони: він прибирає цей хаос до того, як він почне ламати вам сервіс.
L7 HTTP flood — найпідступніший. Канал може бути не забитий, але сайт вмирає “зсередини”: різко росте час відповіді, база отримує сотні/тисячі однотипних запитів, PHP-FPM/Node воркери забиваються, і в якийсь момент все починає повільно розвалюватися. Дуже характерна ознака — атака б’є у конкретні URI: /login, /wp-login.php, /xmlrpc.php, /search, /api/*, сторінки з фільтрами, кошик/чекаут. Якщо в логах видно, що 70–90% трафіку йде в один-два маршрути — це майже завжди L7 історія.
Cloudflare під різні сайти: не “включив і забув”, а як зробити правильно
Якщо у тебе WordPress.
У WordPress є дві класичні болючі точки: wp-login.php і xmlrpc.php. Більшість ботів починають саме звідси. У Cloudflare ми зазвичай робимо так: логін захищаємо правилами (челендж/ліміти), xmlrpc.php — або жорстко обмежуємо, або закриваємо повністю, якщо він не потрібен. Важливо, щоб це не перетворилося на “капча кожному”: для нормальних користувачів краще працює м’яке rate limiting плюс челендж тільки при підозрілій поведінці.
Ще одна річ: типові плагіни додають купу AJAX-ендпойнтів. Якщо сайт під атакою, будь-який “дорогий” AJAX стає ціллю. Тому ми радимо подивитися, які саме URI найчастіше запитуються, і точково поставити правила на них, а не різати весь сайт одним махом.
Якщо у тебе Laravel (або будь-який MVC із сесіями).
Найчастіше б’ють у /login, /register, /password/*, а також у API-ендпойнти, якщо вони доступні публічно. Тут Cloudflare має працювати як периметр, який не пускає “сміття” до того, як воно дійде до застосунку.
Ключовий момент — захистити “дорогі” дії: авторизацію (bcrypt, сесії, CSRF), сторінки з важкими вибірками, маршрути, що тригерять нотифікації або будь-які зовнішні інтеграції. Для Laravel ми часто рекомендуємо:
-
rate limiting на логін/реєстрацію;
-
окремі правила на API;
-
і дуже бажано — закритий origin (тільки Cloudflare IP).
Плюс у самій апці: якщо є можливість, робити прогресивний захист (після кількох підозрілих спроб показати додаткову перевірку), і не виконувати “дорогу” логіку до того, як користувач пройшов базові фільтри.
Якщо у тебе чисте API (мобайл/SPA/партнери).
У API найболючіше — коли один ключ або один клієнт може викликати ендпойнт без обмежень. Тут Cloudflare зручно використати як “шлюз”: лімітувати частоту, відсікати аномальні патерни, а для критичних методів (POST/PUT) додати ще жорсткіші пороги.
І дуже важливо: API повинно мати внутрішні ліміти теж. Тобто Cloudflare — це перший бар’єр, а другий — ваш бекенд (наприклад, ліміти по токену/ключу, блокування при аномалії, захист від повторів, валідація тіла, обмеження розміру payload).
Nginx під L7: як зробити, щоб захист не “вбив” нормальних людей
Тут головна ідея — ставити обмеження не “по всьому сайту”, а на конкретні місця. Логін — лімітуємо, пошук — лімітуємо, API — лімітуємо. А контентні сторінки краще підтримати кешем і швидкою віддачею.
Під час атак дуже рятує “підчищення” таймаутів і keepalive. Коли з’єднання висять занадто довго, воркери закінчуються, і сайт починає падати навіть від помірного навантаження. Тому в штормових ситуаціях краще коротше тримати ресурси і швидко відпускати підозрілих клієнтів.