Артём Шумейко
@artemshumeiko
Как защитить приложение от DoS атак
Сегодня речь пойдет о том, как мы боролись с юзерами, кто тильтует и начинает спамить наш бэкенд мусорными запросами. Но обо всем по порядку.
Когда я только начинал строить Солвит, я понимал, что аудиторией сайта будут в основном айтишники и разработчики, которые с удовольствием поковыряют мою апишку. К тому же последнее, с чем я хотел разбираться — это атаки мамкиных хацкеров на мою платформу для подготовки к собеседованию.
Так вот, первая линия обороны в нашем случае — это веб-сервер nginx, в котором стоит ограничение на количество запросов с одного айпишника. Это прописывается прямо в конфиге
Помимо защиты от большого количества запросов, есть еще один вид атаки — медленные запросы. Это когда юзер сидит на 3G интернете, и каждый запрос длится по 10-15 секунд вместо обычных 50-100 мс. Причем в реальности, тех, кто готов ждать ответа по 10 секунд крайне мало, обычно таким промышляют всякие чудики-злоумышленники. На такой случай в nginx тоже можно поставить ограничение — таймаут, чтобы веб-сервер не захлебнулся запросами и отсекал медленные соединения по таймауту в, например, 10 секунд.
Следующий уровень ограничения хранится уже внутри самого приложения. В нашем случае, часть эндпоинтов на FastAPI используют кастомную зависимость для ограничения количества запросов. Выглядит это довольно лаконично:
Внутри зависимости скрыта логика проверки количества запросов по всем ip-адресам за последние 5 секунд. Суть в том, что часть эндпоинтов исполняют тяжелый и долгий код, поэтому здесь нужно довольно сильное ограничение. Например, мы запускаем код пользователя в изолированной среде на другом сервере, запускаем все тесты, и ждем вебхук-уведомления от сервера с результатами запуска пользовательского кода. Это затратно по времени и ресурсам, поэтому с каждого ip-адреса можно отправлять ограниченное количество запросов.
Изначально мы не вводили такое ограничение, но со временем выяснился занимательный факт:
Как итог, мы еще и на фронте забацали ограничение, чтобы пока обрабатывается текущий код, нельзя было прислать новый. Вот такой способ борьбы с подгоревшими попками) Но я их понимаю, сам бываю таким, когда не получается решить задачу 😅
С технической точки зрения, мы храним в Redis ключ формата
А знаете, что самое классное? Скоро выложу видео с написанием собственного ограничителя запросов с использованием Redis. Можно будет брать и сразу использовать в своих проектах. Ну круто же?)
И многие из вас даже не знали о существовании одной супер удобной структуре данных в Redis) Ждете? Поставьте много огонечков 🔥
Сегодня речь пойдет о том, как мы боролись с юзерами, кто тильтует и начинает спамить наш бэкенд мусорными запросами. Но обо всем по порядку.
Когда я только начинал строить Солвит, я понимал, что аудиторией сайта будут в основном айтишники и разработчики, которые с удовольствием поковыряют мою апишку. К тому же последнее, с чем я хотел разбираться — это атаки мамкиных хацкеров на мою платформу для подготовки к собеседованию.
Так вот, первая линия обороны в нашем случае — это веб-сервер nginx, в котором стоит ограничение на количество запросов с одного айпишника. Это прописывается прямо в конфиге
nginx.conf и выставляется отдельно для GET и POST запросов. Мы разрешаем около 30 GET запросов в секунду, и около 5 POST запросов в секунду. Это позволяет комфортно пользоваться платформой, даже при быстром переключении между основными разделами сайта.Помимо защиты от большого количества запросов, есть еще один вид атаки — медленные запросы. Это когда юзер сидит на 3G интернете, и каждый запрос длится по 10-15 секунд вместо обычных 50-100 мс. Причем в реальности, тех, кто готов ждать ответа по 10 секунд крайне мало, обычно таким промышляют всякие чудики-злоумышленники. На такой случай в nginx тоже можно поставить ограничение — таймаут, чтобы веб-сервер не захлебнулся запросами и отсекал медленные соединения по таймауту в, например, 10 секунд.
Следующий уровень ограничения хранится уже внутри самого приложения. В нашем случае, часть эндпоинтов на FastAPI используют кастомную зависимость для ограничения количества запросов. Выглядит это довольно лаконично:
@router.post(
"/coding/run",
dependencies=[Depends(coding_run_limit)], # <-- вот
)
async def get_coding_result(...):Внутри зависимости скрыта логика проверки количества запросов по всем ip-адресам за последние 5 секунд. Суть в том, что часть эндпоинтов исполняют тяжелый и долгий код, поэтому здесь нужно довольно сильное ограничение. Например, мы запускаем код пользователя в изолированной среде на другом сервере, запускаем все тесты, и ждем вебхук-уведомления от сервера с результатами запуска пользовательского кода. Это затратно по времени и ресурсам, поэтому с каждого ip-адреса можно отправлять ограниченное количество запросов.
Изначально мы не вводили такое ограничение, но со временем выяснился занимательный факт:
часть пользователей, когда не могут справиться с решением задачи, начинают атаковать кнопку с отправкой своего кода и штурмуют бэкенд запросами.
Как итог, мы еще и на фронте забацали ограничение, чтобы пока обрабатывается текущий код, нельзя было прислать новый. Вот такой способ борьбы с подгоревшими попками) Но я их понимаю, сам бываю таким, когда не получается решить задачу 😅
С технической точки зрения, мы храним в Redis ключ формата
rateLimit:<ip_address> и значение — количество запросов за последние 5 секунд. Если число превышает ограничение на бэке, то юзер получает 429 ошибку a.k.a. "иди остынь". Еще один плюс использования Redis в том, что в нем есть встроенная логика очистки значения через определенное время (TTL), чтобы не заниматься очисткой самим на бэкенде. То есть спустя 5 секунд юзер снова может отправлять запросы на проверку своего кода на бэк.А знаете, что самое классное? Скоро выложу видео с написанием собственного ограничителя запросов с использованием Redis. Можно будет брать и сразу использовать в своих проектах. Ну круто же?)
И многие из вас даже не знали о существовании одной супер удобной структуре данных в Redis) Ждете? Поставьте много огонечков 🔥
🔥 247
❤ 31
👍 16
9
😁 3
35 120 11.7K
Обсуждение 35
Обсуждение не доступно в веб-версии. Чтобы написать комментарий, перейдите в приложение Telegram.
Обсудить в Telegram