Усі тести проходять у стейджингу. А потім продакшен відкривають — клієнти, скрепери, «дослідники безпеки» і все, що того дня мешкає в просторі публічних IP. Ви додаєте WAF і обмеження швидкості. Попередження стихають. А потім черга сапорту загоряється: «Не можу увійти», «Оплата не проходить», «API каже 429», «Наш офісний IP заблоковано», «Ваш сайт ненавидить готелі».
Це та частина, яку ніхто не показує в демо продукту: обмеження швидкості й WAF — це не запобіжні поручні, це кермо. Якщо ви ним не керуєте — якщо не спостерігаєте, не налаштовуєте і не моделюєте реальну поведінку користувачів — ви обовʼязково заблокуєте тих, хто платить ваші рахунки, поки боти спокійно змінюють IP і продовжують роботу.
Працююча ментальна модель: WAF проти обмеження швидкості проти «просто зворотний проксі»
Покладіть модні слова на полицю на хвилину й подумайте у термінах режимів відмови.
Обмеження швидкості
Обмеження швидкості — це про захист ємності і про підвищення вартості зловживання. Воно відповідає на питання: «Скільки запитів ми дозволяємо від одного актора за певне вікно?» Воно не відповідає на питання: «Чи цей запит є шкідливим?» Також воно не завжди зупиняє розподілені атаки (ботнет може бути чемним по кожному IP і водночас плавити вас агреговано).
WAF (веб-аплікаційний фаєрвол)
WAF — це про ризик на вході. Він відповідає: «Чи цей запит схожий на спробу експлуатації або зловживання протоколом?» Це пошук по шаблонах і виявлення аномалій — іноді працює, іноді дивно помиляється. WAF зменшують фоновий шум атак (розсилка SQL інʼєкцій, перевірки обходу шляхів), але вони також карають незвичний, але легітимний трафік: API з дивними query-рядками або користувачів за корпоративними проксі.
Зворотний проксі
Зворотний проксі — це ваш регулювальник трафіку: термінація TLS, маршрутизація, нормалізація заголовків, пулінг зʼєднань і місце, де можна реалізувати і WAF-подібну фільтрацію, і обмеження швидкості. Якщо ви запускаєте Docker, зворотний проксі не є опцією — хіба вам подобається виставляти кожен порт контейнера в інтернет як у 2014 році.
Моя думка: розміщуйте логіку обмежень швидкості й WAF на межі, яку ви можете спостерігати й версіонувати. Це означає проксі, яким ви керуєте (Nginx/HAProxy/Traefik) або керований edge (CDN/WAF-провайдер), де ви все ще отримуєте логи і можете поступово впроваджувати зміни. «Чорний ящик безпеки» — це шлях до дебагу продакшену з відчуттям невизначеності.
Цитата, яку варто мати на моніторі: «Надія — це не стратегія.» — приписують різним операторам; сприймайте як переформульовану ідею, а не писання священне.
Факти та історичний контекст (те, що пояснює сучасний біль)
- WAF стали мейнстримом після ранніх веб-експлойтів 2000-х: OWASP почався у 2001 році; перший OWASP Top 10 зʼявився у 2003 і формував набори правил WAF протягом двох десятиліть.
- ModSecurity почався як модуль Apache (2002): він популяризував модель «движка правил», яка згодом зʼявилася в Nginx, Ingress-контролерах та керованих WAF.
- Обмеження швидкості передує вебу: алгоритми token bucket і leaky bucket походять із телекомунікацій/мереж 1980-х і залишаються основними примітивами й сьогодні.
- HTTP/1.1 keep-alive змінив розрахунки: коли клієнти можуть повторно використовувати зʼєднання, ліміти по зʼєднанню стали марними, а контролі за запитом/ідентичністю — важливішими.
- NAT заповнив усюди: мобільні оператори й корпоративні мережі дедалі частіше ставлять тисячі користувачів за кількома еґрес-IP, тому «за IP» почало карати невинні маси.
- CDN змістили проблему «реального IP клієнта»: коли трафік завершується на CDN/WAF, ваш origin бачить IP CDN, якщо ви явно не довіряєте й не парсите форвард-headers.
- HTTP/2 мультиплексинг дозволяє великі швидкості запитів на одне зʼєднання: якщо ви лімітуєте лише зʼєднання, клієнт може все одно відправити потік запитів.
- Контейнери зробили «edge» рухомою ціллю: у епоху віртуальних машин балансувальник був стабільний; у контейнерній моделі команди пробують засовувати безпеку всередину аплікаційного контейнера. Це мило. Не робіть так.
Жарт #1: Набір правил WAF схожий на кімнатну рослину — ігноруй її місяць, і вона згине, можливо забравши з собою ваш вікенд.
Рекомендовані архітектурні патерни (і коли обрати кожен)
Патерн A: CDN/WAF провайдер → зворотний проксі → Docker-сервіси
Це налаштування «дорослого нагляду». Ваш керований edge поглинає обʼємний сміттєвий трафік і справляється з базовою міграцією ботів. Зворотний проксі застосовує точну маршрутизацію, обмеження, що розуміють додаток, і генерує логи, які можна корелювати.
Використовуйте, коли: ви інтернет-фейсинг, у вас є ендпойнти входу/оплати/API, і вам важливо не прокидатися від глобального сканування, що виводить систему з ладу.
Головний ризик: неправильна довіра до форвард-headers. Якщо ви вважаєте будь-який X-Forwarded-For істинним, нападники підроблять «реальний IP» і обійдуть ліміти по IP.
Патерн B: зворотний проксі з WAF-модулем (наприклад, ModSecurity/Coraza) → Docker-сервіси
Це дає контроль на рівні правил і зберігає логи локально. Це також операційно важче: правила потребують налаштування, і ви несете відповідальність за продуктивність.
Використовуйте, коли: вимоги відповідності вимагають самокерованого контролю, або ви не можете поставити керований edge з бізнес-причин.
Головний ризик: високе навантаження на CPU і помилкові спрацьовування, якщо ви ввімкнете повну параною CRS без розуміння власного трафіку.
Патерн C: обмеження швидкості на рівні аплікації всередині контейнера
Добре для логіки «один користувач не має спамити один ендпойнт» (наприклад, скидання пароля). Погано як основний захист.
Використовуйте, коли: потрібні ліміти, що залежать від ідентичності (ID акаунту, API-ключ) і ви не можете чітко висловити це на проксі.
Головний ризик: кожна репліка лімітує незалежно, якщо ви не використовуєте спільне сховище (Redis тощо). Атакуючі просто роблять round-robin.
Що я рекомендую для більшості Docker-команд
Робіть і edge, і origin-контролі:
- На edge: груба міграція ботів і захист від обʼємних атак.
- На зворотному проксі: точні по-роутові ліміти та базові WAF-перевірки, налаштовані під ваш додаток.
- В аплікації: цілеспрямовані, ідентифіковані обмеження для критичних робочих потоків.
Багаторівневий захист. Вимірюваний. З логами. Що можна відкотити.
Ідентичність складна: IP брешуть, користувачі мігрують, а NAT псує вам життя
Більшість самостворених аварій WAF/лімітів походить від однієї припущення: «IP адресу = користувач». У 2026 році це припущення часто невірне і завдає шкоди.
Чому обмеження за IP все ще важливі
Вони прості й дешеві. Вони зупиняють найледачіших сканерів. Зменшують фоновий шум. Але це не справедливість — це грубий інструмент.
Де обмеження за IP провалюються
- Carrier-grade NAT (мобільні мережі): тисячі пристроїв за кількома IP.
- Корпоративні проксі: цілий офіс виглядає як одна адреса. Вітаємо, ви щойно полімітували бухгалтерію.
- Приватні ретранслятори й VPN: легітимні користувачі можуть виглядати «як боти», а боти — «легітимними».
- IPv6: користувачі можуть мати багато адрес; наївна логіка «по IP» обходиться простим ротуванням адрес.
Кращі ідентифікатори, ніж «джерельний IP»
Оберіть найсильніший ідентифікатор, який ви надійно можете валідовано:
- API-ключ (найкраще для B2B API)
- ID акаунту (після аутентифікації)
- Сесійна cookie (обережно з ботами, що зберігають cookie)
- Фінгерпринт пристрою (корисно, але проблематично й чутливо до приватності)
- IP-підмережа (іноді справедливіше, ніж точна IP, іноді гірше)
Практичне правило: лімітуйте за IP для неавтентифікованих ендпойнтів, а після аутентифікації переходьте на ідентифікатор-орієнтовані ліміти. Саме там — справедливість.
Проєктування розумних лімітів, які не спалять реальних користувачів
Обмеження швидкості — це не про вибір числа. Це про моделювання поведінки: людські потоки, повторні спроби з боку додатка, нестабільність мобільних мереж і сторонні інтеграції, що ведуть себе як схвильовані білки.
Починайте з класів ендпойнтів, а не з глобального ліміту
- Логін: низький ліміт, висока чутливість. Додайте прогресивну затримку та сильні перевірки ботів.
- Скидання пароля / OTP: дуже низький ліміт, суворий аудит.
- Пошук: середній або високий, але захищайте бекенд-каші та бази даних.
- Статичні ресурси: зазвичай обробляються CDN; не витрачайте ліміт на origin тут.
- API для масових операцій: дозволяйте спалахи, штрафуйте стійкі швидкості і вимагайте ключів.
- Вхідні webhook: лімітуйте за ідентичністю партнера, а не за IP, і дозволяйте повтори.
Обирайте алгоритм, що відповідає вашому трафіку
Token bucket зазвичай підходить: дозволяє короткі спалахи (завантаження сторінки, запуск додатка), обмежує стійке зловживання. Фіксоване вікно просте, але створює кромку: користувачі блокуються, бо натиснули «оновити» в невдалу секунду.
Робіть відповіді 429 корисними
Якщо ви повертаєте HTTP 429, включіть Retry-After (в секундах) або схему заголовків rate limit, яку ви дійсно дотримуєтеся. Реальні клієнти відкотяться. Погані клієнти ігнорують — і це нормально; ви все одно захищаєте бекенд.
Не лімітуйте health checks так, ніби це нападники
Якщо ваш оркестратор або зовнішній монітор отримає 429, ви створите самопідсилювану проблему: він буде повторювати частіше, і тепер вас «DDoSить» власний інструмент.
Визначте, що означає «заблоковано»
Блокування може бути:
- Жорстка відмова (403/429): підходить для відомо-шкідливої поведінки.
- Мʼякий виклик: CAPTCHA або JS-челендж на edge; корисно для підозрілого, але не остаточно шкідливого трафіку.
- Погіршення сервісу: віддавати кешований/застарілий результат, відключати дорогі фічі або ставити в чергу роботу.
Тюнінг WAF без містики
Набір правил WAF — це гіпотеза. Ваш трафік — це експеримент. Підходьте до тюнінгу інженерно, а не як до фольклору.
Спочатку запускайте у режимі тільки виявлення
Увімкніть WAF, щоб логував, що би він заблокував. Зберіть принаймні кілька днів трафіку, включно з піковими годинами і пакетними задачами. Потім налаштуйте винятки на підставі доказів.
Знайте зони з високим рівнем хибних спрацьовувань
- JSON API з вкладеними payload-ами: можуть тригерити правила «аномалії тіла запиту».
- GraphQL: query-рядки часто виглядають як патерни інʼєкцій.
- Пошукові фільтри: користувачі вставляють дивні символи — WAF панікує.
- Ендпойнти для завантаження файлів: парсинг multipart і ліміти розміру.
- Параметри redirect/callback: закодовані URL у параметрах виглядають як SSRF-проби.
Стратегія винятків: вузькі, логовані, переглянуті
Не відключайте цілі класи правил глобально, бо один ендпойнт шумить. Вирізайте винятки за:
- Шляхом (точний route)
- Методом (POST проти GET)
- Імʼям параметра (дозвольте дивність лише в
qдля пошуку, а не вredirect) - Content-type (JSON проти form)
- Аутентифікований vs неаутентифікований
Продуктивність WAF має значення
Інспекція WAF може сильно навантажувати CPU. Якщо ваш проксі насичений, ви побачите зростання латентності, а потім хвилю таймаутів. Це виглядає як інцидент аплікейшена, але насправді — ваш рівень безпеки душить хост.
Жарт #2: Найшвидший спосіб знайти нетестоване правило WAF — розгорнути його в пʼятницю після обіду. Це також найшвидший спосіб зустріти вашу ротацію on-call.
Практичні завдання: команди, виводи та рішення (12+)
Це ті завдання, які ви виконуєте, коли користувачі скаржаться на блокування, боти підскакують, або ви впроваджуєте контролі вперше. Команди написані для Docker-хоста зі зворотним проксі-контейнером (Nginx/Traefik/HAProxy) і типових Linux-утиліт.
Завдання 1: Підтвердити, які порти насправді відкриті
cr0x@server:~$ docker ps --format 'table {{.Names}}\t{{.Image}}\t{{.Ports}}'
NAMES IMAGE PORTS
edge-nginx nginx:1.25 0.0.0.0:80->80/tcp, 0.0.0.0:443->443/tcp
app-api myco/api:2026.01 8080/tcp
app-web myco/web:2026.01 3000/tcp
redis redis:7 6379/tcp
Що це означає: тільки edge-проксі виставляє 80/443 у світ. Аплікаційні контейнери внутрішні.
Рішення: якщо ви бачите 0.0.0.0:8080->8080 на ап-контейнері — виправляйте. Ліміти і WAF не допоможуть, якщо нападники обходять проксі.
Завдання 2: Перевірити, що проксі — єдиний слухач на 80/443
cr0x@server:~$ sudo ss -lntp | egrep ':80|:443'
LISTEN 0 4096 0.0.0.0:80 0.0.0.0:* users:(("docker-proxy",pid=2114,fd=4))
LISTEN 0 4096 0.0.0.0:443 0.0.0.0:* users:(("docker-proxy",pid=2121,fd=4))
Що це означає: Docker публікує порти, швидше за все, змеплені на проксі-контейнер.
Рішення: якщо інші процеси слухають ці порти, у вас може бути шлях обходу або колізія портів. Виправте перед тим, як налаштовувати правила WAF.
Завдання 3: Перевірити, чи ви правильно довіряєте форвард-headers
cr0x@server:~$ docker exec -it edge-nginx nginx -T 2>/dev/null | egrep -n 'real_ip|set_real_ip_from|X-Forwarded-For' | head -n 30
45: real_ip_header X-Forwarded-For;
46: set_real_ip_from 172.18.0.0/16;
47: real_ip_recursive on;
Що це означає: Nginx налаштований сприймати X-Forwarded-For як клієнтський IP, але лише коли безпосередній відправник у 172.18.0.0/16 (ваша Docker-мережа).
Рішення: добре. Якщо ви бачите set_real_ip_from 0.0.0.0/0;, ви дозволяєте нападникам підробляти IP і уникати лімітів. Виправте негайно.
Завдання 4: Підтвердити, який IP бачить аплікація (перевірка «чому користувачі згруповані?»)
cr0x@server:~$ docker logs --tail=20 app-api
2026-01-03T08:18:22Z INFO request method=GET path=/v1/me remote=172.18.0.5 xff="198.51.100.23"
2026-01-03T08:18:22Z INFO request method=POST path=/v1/login remote=172.18.0.5 xff="203.0.113.10, 172.18.0.1"
Що це означає: TCP-пір аплікації — проксі, але вона також отримує X-Forwarded-For. Ланцюжок вказує на кілька хопів.
Рішення: переконайтеся, що ваш фреймворк аплікації використовує правильну логіку клієнтського IP і довіряє лише вашому проксі. Інакше ліміти в апі будуть несправедливі або обходжувані.
Завдання 5: Визначити найактивніших відправників на edge (за IP) з access-логів
cr0x@server:~$ sudo awk '{print $1}' /var/log/nginx/access.log | sort | uniq -c | sort -nr | head
9123 203.0.113.77
5110 198.51.100.23
4022 203.0.113.10
1889 192.0.2.44
Що це означає: ці IP генерують багато запитів. Не обовʼязково зловмисні; може бути NAT або партнер.
Рішення: перевірте user-agent, шляхи та коди відповідей перед блокуванням. Великий обсяг сам по собі — не провина.
Завдання 6: Знайти, які ендпойнти тригерять 429
cr0x@server:~$ sudo awk '$9==429 {print $7}' /var/log/nginx/access.log | sort | uniq -c | sort -nr | head
644 /v1/login
201 /v1/search
88 /v1/password-reset
Що це означає: ваш поточний ліміт найчастіше спрацьовує на логіні й пошуку.
Рішення: 429 на логіні можуть бути корисними (контроль ботів) або шкідливими (реальні користувачі за NAT). 429 на пошуку часто означають, що ліміт занадто низький для поведінки UI або фронтенд занадто агресивно повторює запити.
Завдання 7: Перевірити, чи 429 корелюють з певним user-agent (підпис бота)
cr0x@server:~$ sudo awk '$9==429 {print $12}' /var/log/nginx/access.log | sort | uniq -c | sort -nr | head
701 "-"
133 "python-requests/2.31.0"
67 "Mozilla/5.0"
Що це означає: багато 429 мають відсутній/порожній user-agent, що характерно для скриптів.
Рішення: ви можете додати суворіші ліміти або челенджі для трафіку з порожнім UA, будьте обережні, щоб не порушити легітимні клієнти (деякі корпоративні інструменти дуже мінімалістичні).
Завдання 8: Підтвердити, чи проксі обмежений по CPU під час інспекції WAF
cr0x@server:~$ docker stats --no-stream edge-nginx
CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS
a1b2c3d4e5f6 edge-nginx 186.45% 612MiB / 2GiB 29.88% 1.8GB / 2.1GB 0B / 0B 38
Що це означає: майже 2 ядра насичені в контейнері проксі. Це може бути парсинг WAF, правила з важкими regex або просто забагато трафіку.
Рішення: якщо піки CPU збігаються з блокуваннями й латентністю, налаштуйте правила WAF (звузьте область), додайте кеш/CDN або масштабируйте проксі-реліки за балансувальником.
Завдання 9: Перевірити симптоми conntrack і беклогу сокетів (edge-хост під стресом)
cr0x@server:~$ sudo sysctl net.netfilter.nf_conntrack_count net.netfilter.nf_conntrack_max
net.netfilter.nf_conntrack_count = 249812
net.netfilter.nf_conntrack_max = 262144
Що це означає: conntrack майже заповнений. Нові зʼєднання можуть відмовлятися або отримувати дивні таймаути.
Рішення: обережно підніміть conntrack max, зменшіть churn зʼєднань (keep-alive) і переконайтеся, що обмеження не штовхають клієнтів у шторми повторного зʼєднання.
Завдання 10: Проінспектувати логи WAF для точної ідентифікації правила, що блокує реальних користувачів
cr0x@server:~$ sudo tail -n 20 /var/log/modsecurity/audit.log
--b7c1f3-A--
[03/Jan/2026:08:20:11 +0000] WQx8cQAAAEAAAB9J 198.51.100.23 54022 172.18.0.10 443
--b7c1f3-B--
GET /v1/search?q=%2B%2B%2B HTTP/1.1
Host: api.example.internal
User-Agent: Mozilla/5.0
--b7c1f3-F--
HTTP/1.1 403
--b7c1f3-H--
Message: Access denied with code 403 (phase 2). Matched "Operator `Rx' with parameter `(?:\bunion\b|\bselect\b)'" against variable `ARGS:q' (Value: '+++') [id "942100"]
Що це означає: правило 942100 спрацювало на параметрі пошуку q. Користувач шукав «+++» і потрапив під підозру як SQLi.
Рішення: додайте цілеспрямований виняток для ARGS:q на /v1/search (або зменшіть параноїчність для цього ендпойнта). Не вимикайте цілий набір правил SQLi глобально.
Завдання 11: Перевірити, що ключ для rate limiting — це те, що ви думаєте
cr0x@server:~$ docker exec -it edge-nginx nginx -T 2>/dev/null | egrep -n 'limit_req_zone|limit_req' | head -n 40
120: limit_req_zone $binary_remote_addr zone=perip:20m rate=10r/s;
215: location /v1/login {
216: limit_req zone=perip burst=20 nodelay;
217: proxy_pass http://app-api:8080;
218: }
Що це означає: ключ — це IP клієнта ($binary_remote_addr). Якщо ваш «реальний IP» неправильний, багато користувачів злипаються в один бакет.
Рішення: підтвердіть обробку реального IP спочатку. Якщо багато легітимних користувачів ділять IP (корпоративний NAT), перенесіть ліміти на комбінацію IP + cookie або додайте ідентифіковані ліміти в аплікації.
Завдання 12: Відтворити 429 зі своєї робочої станції, щоб підтвердити поведінку й заголовки
cr0x@server:~$ for i in {1..40}; do curl -sk -o /dev/null -w "%{http_code} %{time_total}\n" https://example.com/v1/login; done | tail
200 0.031122
200 0.030881
429 0.000942
429 0.000913
429 0.000901
Що це означає: після кількох запитів ви досягаєте ліміту і отримуєте миттєві 429.
Рішення: перевірте, чи 429 містить Retry-After. Якщо ні — додайте. Якщо клієнти далі штурмують після 429, ви отримаєте петлю зворотного звʼязку.
Завдання 13: Перевірити, що аплікація не генерує внутрішні повтори, що виглядають як зловживання
cr0x@server:~$ docker logs --since=10m app-web | egrep -i 'retry|429|too many' | tail -n 20
2026-01-03T08:14:02Z WARN api_call failed status=429 retry_in_ms=200
2026-01-03T08:14:02Z WARN api_call failed status=429 retry_in_ms=200
2026-01-03T08:14:03Z WARN api_call failed status=429 retry_in_ms=200
Що це означає: фронтенд швидко (200ms) повторює при 429 — це протилежне бажаному.
Рішення: виправте логіку відступу клієнта (експоненційна, поважати Retry-After). Інакше ваш ліміт перетвориться на шторм повторів.
Завдання 14: Підтвердити точку термінації TLS і протокол (HTTP/2 може змінити патерни навантаження)
cr0x@server:~$ echo | openssl s_client -alpn h2 -connect example.com:443 2>/dev/null | egrep 'ALPN protocol|Protocol'
Protocol : TLSv1.3
ALPN protocol: h2
Що це означає: клієнти домовились про HTTP/2. Мультиплексинг може збільшити паралелізм запитів від клієнта.
Рішення: переконайтеся, що ваші ліміти привʼязані до ідентичності й до запиту, а не до зʼєднання. Якщо ви обмежуєте лише зʼєднання, HTTP/2 пройде крізь.
Завдання 15: Перевірити Docker-мережеві шляхи (переконатися, що немає прямого маршруту до апів ззовні)
cr0x@server:~$ docker network inspect bridge --format '{{json .IPAM.Config}}'
[{"Subnet":"172.17.0.0/16","Gateway":"172.17.0.1"}]
Що це означає: контейнери на приватному Docker-bridge. Це нормально, але не плутайте «приватну IP» з «недоступною».
Рішення: підтвердьте, що правила файрволу блокують зовнішній доступ до опублікованих портів контейнера, і уникайте публікації портів аплікацій взагалі.
Швидкий план діагностики
Коли реального користувача заблоковано (або графіки виглядають як сейсмограф), не починайте з редагування правил. Почніть з пошуку шару, що спричиняє проблему.
По-перше: визначте, який це тип блокування
- 403: ймовірно WAF/ACL/чорний список.
- 429: обмеження швидкості.
- 5xx: падіння upstream, перевантаження або таймаути проксі.
- Таймаути клієнта: conntrack/backlog/TLS handshake проблеми або CPU проксі в естонажі.
По-друге: знайдіть, де прийнято рішення
- Логи/події edge-провайдера (якщо у вас є CDN/WAF).
- Access-логи зворотного проксі: код статусу, час обробки, час upstream.
- WAF audit-логи: ID правила і ціль збігу.
- Логи аплікації: чи дійшов запит до аплікації?
По-третє: валідйте ідентичність і заголовки
- Чи правильний клієнтський IP (ланцюжок
X-Forwarded-For)? - Чи ви довіряєте лише відомим проксі?
- Чи ключ ліміту зливає багато користувачів в один бакет?
По-четверте: перевірте насичені ресурси
- CPU і памʼять проксі (інспекція WAF і regex може підскочити CPU).
- Conntrack близько до max (чурн зʼєднань і SYN-флуди).
- Затримки upstream (база/кеш), що спричиняють шторм повторів.
По-пʼяте: помʼякшіть безпечно
- Перемкніть WAF у режим тільки виявлення тимчасово, якщо хибні спрацьовування серйозні.
- Підніміть burst-ємність для користувацьких ендпойнтів на час розслідування.
- Застосуйте тимчасові allowlists для відомих IP партнерів з обовʼязковим терміном дії.
- Тротлити або викликати челендж для підозрілих класів трафіку (порожній UA, відомі погані ASN за умови впевненості).
Три короткі історії з корпоративного фронту
Інцидент через неправильне припущення: «Per-IP = per-user»
Компанія була середнього розміру, B2C, з великою мобільною базою, і ендпойнт логіна отримав атаку credential stuffing. Вони поставили Nginx-ліміт на /login: 5 запитів на хвилину на IP. Просто. Працювало в синтетичних тестах. Трафік ботів упав, графіки заспокоїлися.
Через два дні сапорт відкрив інцидент: з певного регіону різко зросла кількість відмов у логіні. Інженери дивилися на дашборд WAF і не бачили очевидного. Origin почав повертати 429. «Добре», — хтось сказав, — «боти ще пробують». Але user-agent виглядав як нормальні браузери, і запити мали валідні CSRF-токени.
Відсутній шматок — мережева реальність: великий мобільний оператор у тому регіоні використовував агресивний NAT. Велика кількість легітимних користувачів ділила невеликий пул еґрес-IP. Коли настала пікова година, NAT-IP досяг ліміту. Тисячі людей фактично змагалися за пʼять логінів на хвилину.
Виправлення не було «прибрати ліміти». Воно полягало в тому, щоб «припинити вважати IP за людину». Вони підняли ліміти для неаутентифікованих з більшим burst, додали ключі cookie пристрою на edge, і ввели жорсткіші ліміти після повторних невдалих спроб логіна по акаунту. Також додали мʼякий челендж для підозрілих патернів логіна. Credential stuffing знизився, а легітимні логіни відновилися.
Оптимізація, що відкотилася: кешування не того шару
Інша команда хотіла зменшити навантаження на бекенд і покращити латентність. Вони ввели агресивне кешування на зворотному проксі для кількох GET API ендпойнтів. Загалом хороша ідея. Потім вони вирішили кешувати й деякі помилкові відповіді «щоб захистити бекенд під час піків». Це включало 429 відповіді від ліміту.
Під час помірного підвищення трафіку частина користувачів почала отримувати 429. Проксі кешував ті 429 на 30 секунд. Тепер кожен користувач із тим cache key (що включав надто широку множину заголовків) отримував кешований 429, навіть якщо він сам був добре в межах ліміту.
Інцидент виглядав як «лімітер надто жорсткий», але не був ним. Слой кешування посилив радіус дії локального троттла і перетворив його на клієнтський аутедж. Відкат був миттєвим: ніколи не кешувати 429 на проксі, якщо у вас немає дуже свідомого дизайну, правильного ключа кешу й вагомої причини. Щоб захистити upstream, використовуйте черги, circuit breakers або віддавайте застаріле вміст — а не кешовані троттли.
Нудна, але правильна практика, що врятувала день: staged rollout з detection-only і кнопкою вбивства
Сервіс, що працював з платежами, мав вимогу безпеки додати WAF перед Docker-хостованими API. Їх вже обпікало «увімкнути і молитися», тому вони зробили двотижневу фазу тільки виявлення з щоденним переглядом.
Щоранку SRE і інженер аплікації дивилися найпоширеніші спрацьовування правил, порівнювали їх з реальними запитами. Вони позначали кожен хіт як «шкідливий», «невизначений» або «хибнопозитивний», і писали винятки тільки коли могли пояснити трафік. Також була feature-flag, яка дозволяла вимкнути enforcement одним перезавантаженням конфига.
В день включення вони робили rollout за класами ендпойнтів: спочатку admin (низька різноманітність користувачів), потім партнерські API (з ключами), потім публічні ендпойнти. Моніторили 403 і 429 і мали чіткі пороги для відкату.
Того ж дня легітимна інтеграція партнера почала падати — вона посилала незвично довгі JSON-поля, що тригерило правило аномалії розміру тіла. Оскільки у них була історія в режимі detection і кнопка вбивства, вони могли чітко виписати виняток для того партнера і параметра, перезавантажити і рухатися далі. Ніяких драм. Жодних war room. Просто робота.
Типові помилки: симптом → корінна причина → виправлення
1) Користувачі за офісами/готелями не можуть увійти (багато 429)
Симптом: скарги з корпоративних мереж; логи показують багато 429 з одного IP з нормальними UA.
Корінна причина: ліміт по IP на неаутентифікованих ендпойнтах; NAT групує багато користувачів.
Виправлення: підвищити burst, лімітувати за cookie/токеном пристрою, додати ліміти на невдалі спроби входу по акаунту в аплікації. Залишайте пер-IP ліміти, але зробіть їх менш каральними.
2) «Все заблоковано» після увімкнення WAF
Симптом: раптовий сплеск 403; аплікація бачить менше запитів; логи WAF показують, що звичайні payload-и тригерять правила.
Корінна причина: включено enforcement без бази виявлення; рівень параної CRS занадто високий; правила застосовані до всіх маршрутів однаково.
Виправлення: повернути в detection-only, налаштувати за ендпойнтом, додати вузькі виключення, потім поступово включати.
3) Ліміти неефективні проти ботів
Симптом: навантаження бекенду все ще високе; per-IP тротли спрацьовують, але трафік ботів триває.
Корінна причина: розподілена атака з ротацією IP; або бот використовує багато IP під порогом.
Виправлення: додати ліміти за ідентичністю (API-ключі, сесії), челенджі на edge, агрегатні ліміти (глобальні обмеження одночасності) і кешування.
4) Випадкові користувачі блокуються з помилкою «SQL injection» на пошуку
Симптом: 403 на пошуку; логи WAF показують правила SQLi на параметрі query.
Корінна причина: загальні CRS-правила трактують синтаксис пошуку як SQLi.
Виправлення: звузьте інспекцію або відключіть конкретні rule ID для того параметра/шляху; залиште SQLi-правила для критичних ендпойнтів.
5) Після додавання лімітів латентність зростає і зʼявляються таймаути
Симптом: p95 latency зростає; CPU проксі високо; зʼявляються 504/499.
Корінна причина: навантаження WAF або правила з важкими regex; також можливий churn зʼєднань, якщо клієнти повторюють запити.
Виправлення: зменшіть область інспекції (інспектуйте тіла запитів лише де потрібно), оптимізуйте правила, масштабируйте проксі і навʼязуйте адекватний backoff клієнтам з Retry-After.
6) Користувачі обходять ліміти, підробляючи X-Forwarded-For
Симптом: логи показують дуже різні клієнтські IP з одного TCP-піра; ліміти не ловлять зловмисників.
Корінна причина: проксі довіряє форвард-headers від недовірених джерел.
Виправлення: налаштуйте діапазони довірених проксі; очищайте вхідні форвард-headers на публічному edge і додавайте свої коректні заголовки.
7) Webhooks падають, бо повтори партнера блокуються
Симптом: вхідний webhook ендпойнт повертає 429; партнер повідомляє про відсутні події.
Корінна причина: webhook лімітується по IP; партнери роблять повторні спроби після таймаутів.
Виправлення: лімітуйте по ідентичності партнера (shared secret, token), дозволяйте більший burst і обробляйте асинхронно з idempotency keys.
8) «Наш моніторинг каже, що ми впали», але користувачі в порядку (або навпаки)
Симптом: зовнішні перевірки блокуються, але реальні користувачі ні; або навпаки — реальні користувачі блокуються, а перевірки ні.
Корінна причина: монітори мають окремі IP/заголовки і трактуються по-іншому; є WAF-винятки для моніторів; ліміти на health-ендпойнтах.
Виправлення: вважайте моніторинг першокласним клієнтом: стабільні ідентифікатори, явні allow-правила і окремі ендпойнти для глибоких перевірок.
Чеклісти / покроковий план
Покроковий rollout, що уникає самостворених аутеджів
- Закрийте експозицію: тільки проксі публікує порти; контейнери внутрішні.
- Нормалізуйте ідентичність клієнта: налаштуйте довірені IP проксі; коректно встановіть реальний IP клієнта; очищайте підроблювані заголовки на публічному edge.
- Увімкніть логування спочатку: access-логи з часом upstream; WAF audit-логи; структуровані логи аплікації.
- Включіть WAF у detection-only: збирайте принаймні кілька днів; включно з піковими годинами і пакетними завданнями.
- Класифікуйте ендпойнти: login, search, checkout, admin, webhooks, bulk APIs. Кожен з політикою.
- Впровадьте ліміти по ендпойнту: почніть з щедрого burst; застосовуйте стійку норму; повертайте 429 з Retry-After.
- Введіть ідентифіковані ліміти: API key/account ID/session після auth; per-IP лише для неаутентифікованого або грубого захисту.
- Створюйте винятки вузько: по шляху + параметру + методу. Логуйте мотивацію для кожного винятку.
- Додайте кнопку вбивства: один конфіг-флаг, щоб вимкнути enforcement або зменшити параною без ребуту аплікацій.
- Розгорніть поступово: admin/internal → партнерські API → публічні ендпойнти.
- Встановіть пороги, сумісні з SLO: визначте, які рівні 403/429 прийнятні перед відкатом.
- Репетируйте інцидентний план: чи можете ви визначити шар блокування за менше ніж 5 хвилин?
Операційний чекліст для постійного обслуговування
- Щотижневий огляд найпоширеніших правил WAF і ендпойнтів з 429.
- Підтвердження конфігурації «реального IP» після будь-яких змін мережі/CDN.
- Відслідковувати хибні позитиви як баги з власниками і датою закінчення тимчасових allowlist.
- Тестувати логін/пошук з мобільних мереж і корпоративних еґресів (або принаймні з проксі, що поводяться схоже).
- Переконатися, що клієнтські SDK поважають Retry-After і використовують експоненційний backoff.
- Планувати ємність CPU проксі для інспекції WAF; ставте це як реальне робоче навантаження.
FAQ
1) Чи ставити WAF всередину ап-контейнера?
Ні, не як основний контроль. Розміщуйте його на зворотному проксі або керованому edge, де можна централізувати політики, консистентно логувати і уникнути дрейфу між репліками.
2) Чи завжди погано обмеження по IP?
Це корисно й дешеве. Це також може бути несправедливим. Використовуйте як грубу зовнішню оболонку, а потім застосовуйте кращі ідентифікатори (cookie/session/API key/account) де можливо.
3) Як уникнути блокування мобільних користувачів за NAT?
Дозволяйте спалахи, не ставте крихітні ліміти на хвилину, і переходьте на ключ cookie/session як можна скоріше. Для логіна додайте ліміти по невдалим спробам на акаунт.
4) Яка різниця між 403 і 429 для цих контролів?
Використовуйте 429 для «сповільніться, ви перевищили квоту». Використовуйте 403 для «ми не обслуговуємо цей запит» (WAF/ACL). Змішування плутає клієнтів і операторів.
5) Чому увімкнення HTTP/2 змінило поведінку лімітів?
HTTP/2 дозволяє багато паралельних запитів по одному зʼєднанню. Якщо ви покладалися на кількість зʼєднань як проксі-навантаження, HTTP/2 ламає цю модель. Лімітуйте по ідентичності/запиту замість цього.
6) Чи може WAF зупинити credential stuffing?
Не надійно сам по собі. Credential stuffing виглядає як нормальний трафік входу. Потрібні ліміти, контролі по акаунту, детекція ботів і бажано MFA/оцінка ризику.
7) Як дізнатися, чи WAF спричиняє латентність?
Дивіться на CPU проксі, час обробки запитів, і чи зростає латентність при стабільному upstream time. WAF audit-логи і поля таймінгу проксі допоможуть корелювати.
8) Який найбезпечніший спосіб додавати винятки для хибних спрацьовувань?
Оберіть вузьку сферу: за маршрутом, методом і імʼям параметра; тримайте виняток якнайменшим; додайте власника і дату перегляду.
9) Чи блокувати по user-agent?
Як сигнал — так; як єдина ідентичність — ні. User-agent легко підробити. Але порожні або явно скриптові UA можуть виправдати суворіші ліміти або челенджі.
10) Як запобігти підробці клієнтського IP через X-Forwarded-For?
Довіряйте форвард-headers лише від відомих IP проксі. Очищайте вхідні форвард-headers на публічному edge і встановлюйте свої канонічні заголовки.
Висновок: наступні кроки, що дійсно зменшують шум в пейджері
Якщо запамʼятаєте одну річ: обмеження швидкості і WAF — це продакшен-фічі. Вони потребують спостережуваності, поетапного rollout і постійного тюнінгу, як і бази даних чи пайплайни деплою.
Зробіть це далі, в порядку:
- Переконайтеся, що тільки edge-проксі відкритий в інтернеті. Ніяких шляхів обходу портів.
- Виправте обробку реального клієнтського IP і межі довіри до форвард-заголовків.
- Впровадьте пер-ендпойнт ліміти з burst і 429 + Retry-After.
- Запустіть WAF у detection-only, відтюніть на основі логів, потім поступово вмикайте enforcement.
- Додайте ідентифіковані ліміти після аутентифікації; припиніть покладатися на IP як «користувач».
- Майте кнопку вбивства і поріг відкату. Ваше майбутнє «я» пошле вам листа подяки.
Мета не в «заблокувати нападників». Це маркетинг. Справжня мета: тримати сервіс здоровим, дозволяючи легітимним користувачам робити звичайні речі. Решта — це тюнінг і смиренність.