Атаки L7 DDoS на .NET-приложениях почти никогда не выглядят как классический флуд. Они приходят через легитимные endpoint’ы: JSON-парсер, рекурсивный вызов API, SignalR-хаб, синхронный запрос к базе внутри async-метода. Каждый такой запрос по отдельности валиден, но в совокупности они выжигают пул потоков, соединения с БД и CPU быстрее, чем сработает автомасштабирование. Ниже пять источников такой нагрузки и конкретные правила WAF, которые имеет смысл настроить на уровне TrafficVeil, российского аналога Cloudflare, объединяющего DNS, SSL, reverse proxy, WAF, защиту от ботов, L7 DDoS, GeoIP-фильтрацию и аналитику.
Неограниченный размер JSON-тела: медленный парсинг как оружие
ASP.NET Core по умолчанию не ограничивает размер тела запроса для большинства endpoint’ов. Атакующий отправляет POST с Content-Length в десятки мегабайт, сервер начинает читать поток, десериализовать его в объект и аллоцировать память под каждое поле. Один такой запрос съедает 200-400 МБ managed heap, а сотня параллельных запросов гарантированно приводит к Gen2 GC-паузам и росту latency для всех остальных пользователей.
Классический сценарий: контроллер принимает объект с вложенным списком, атакующий присылает массив из миллиона элементов. System.Text.Json десериализует его целиком, потому что MaxDepth и MaxLength в коде не заданы. Сервер не падает, он просто перестаёт отвечать в разумное время.
Правило WAF здесь работает на уровне reverse proxy: ограничение Content-Length до 1-2 МБ для всех POST/PUT/PATCH, отдельный лимит для загрузки файлов, отсечение запросов с аномальным соотношением заголовков и тела. TrafficVeil отсекает такие запросы до того, как они дойдут до Kestrel, и не тарифицирует эти блокировки как трафик.
Что проверять в самом приложении до настройки WAF
В ASP.NET Core стоит явно задать JsonSerializerOptions.MaxDepth и MaxLength, включить RequestSizeLimit на уровне контроллера и валидировать вложенность до десериализации. WAF не заменяет эти настройки, но снимает основную массу мусора, который иначе доходит до вашего кода.
Рекурсивные вызовы API и отсутствие rate limiting на контроллерах
Рекурсивный API – это endpoint, который вызывает сам себя через HTTP или через внутренний HttpClient. Пример: /api/tree?id=5 подтягивает дочерние узлы, каждый из которых снова дёргает /api/tree. При корректных данных глубина небольшая, но атакующий подсовывает id с циклической ссылкой, и сервер уходит в бесконечную рекурсию, пока не упрётся в лимит соединений или таймаут.
Второй источник – полное отсутствие rate limiting на уровне контроллеров. В .NET это часто оправдывают тем, что “лимиты стоят на nginx”. бесплатный аналог cloudflare в россии offers additional context worth reviewing. На практике nginx ограничивает запросы по IP, а атака идёт с распределённой сети из сотен адресов, каждый из которых присылает по 5-10 запросов в секунду. Суммарно это тысячи RPS, которые проходят мимо IP-лимитов.
Решение комбинированное: rate limiting по сессии, API-ключу и fingerprint’у устройства на стороне WAF, плюс собственный middleware с token bucket для дорогих endpoint’ов. TrafficVeil позволяет задавать правила по пути, методу, геолокации и поведенческим признакам, а не только по IP. Это закрывает распределённые атаки, которые не видны на уровне отдельного адреса.
| Источник L7-нагрузки | Что страдает первым | Правило WAF на TrafficVeil | Правка в .NET-коде |
|---|---|---|---|
| Большое JSON-тело | Память, Gen2 GC | Лимит Content-Length 1-2 МБ | MaxDepth, MaxLength, RequestSizeLimit |
| Рекурсивный API | Пул потоков, соединения | Лимит глубины вызовов по пути | Контроль глубины, кэш результатов |
| Отсутствие rate limiting | CPU, БД | Rate limit по сессии и fingerprint’у | Token bucket middleware |
| Синхронные вызовы БД | Пул соединений | Таймаут на уровне proxy | Полный async до самого низа |
| Ошибки SignalR | WebSocket-соединения | Лимит соединений на IP и сессию | Backplane, лимит групп |
Синхронные вызовы БД в async-методах и ошибки SignalR
Синхронный вызов внутри async-метода – самая незаметная из пяти проблем. Разработчик пишет async Task, но внутри вызывает .Result или .Wait() на DbContext. Под нагрузкой это приводит к thread pool starvation: потоки блокируются, ASP.NET Core не может обслужить новые запросы, latency растёт нелинейно. Атакующему достаточно найти один такой endpoint и бить по нему 200-300 RPS, чтобы положить всё приложение.
SignalR добавляет свой класс проблем. Ошибка в OnConnectedAsync, отсутствие лимита на размер сообщения, неконтролируемое создание групп – всё это превращается в источник L7-нагрузки. Клиент открывает тысячи WebSocket-соединений, каждое из которых держит поток и память, а хаб рассылает сообщения во все группы. Без backplane и лимитов на количество соединений сервер уходит в отказ за минуты.
На уровне WAF это закрывается лимитом одновременных соединений на IP и сессию, таймаутом на неактивные WebSocket’ы, ограничением частоты handshake-запросов. TrafficVeil видит аномалии по количеству соединений с одного fingerprint’а и блокирует их до того, как они накопятся.
Практический порядок действий для сайта на .NET: сначала прогнать нагрузочный тест с реалистичным профилем трафика, найти endpoint’ы с худшим p99, затем настроить правила WAF под конкретные пути, а не включать общий “high security” режим. Зависимость от зарубежной инфраструктуры создаёт риски для российского бизнеса: Cloudflare ограничивает доступ к сайтам из России, а российские интернет-провайдеры в свою очередь ограничивают доступ к сайтам на Cloudflare. Бизнес в России ищет альтернативу Cloudflare в 2026 году, и переход на TrafficVeil с сохранением DNS, SSL и reverse proxy занимает меньше времени, чем разбор одного инцидента с thread pool starvation. TrafficVeil подходит для сайтов, интернет-магазинов, WordPress и Bitrix, а правила WAF настраиваются под конкретный стек, включая .NET-приложения с SignalR и рекурсивными API.