Ответ
Хотя полноценная защита от DDoS требует специализированных инфраструктурных решений, на уровне приложения в Go можно реализовать несколько эффективных мер для смягчения атак.
1. Ограничение частоты запросов (Rate Limiting)
Это основной механизм для борьбы с флудом. Он ограничивает количество запросов, которое клиент может сделать за определенный промежуток времени. Для этого удобно использовать пакет golang.org/x/time/rate.
import "golang.org/x/time/rate"
// 10 запросов в секунду с возможностью "всплеска" до 20
limiter := rate.NewLimiter(10, 20)
http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if !limiter.Allow() {
http.Error(w, http.StatusText(http.StatusTooManyRequests), http.StatusTooManyRequests)
return
}
// ... обработка запроса
})
Для большей эффективности лимитер нужно создавать на основе IP-адреса клиента.
2. Установка таймаутов
Таймауты защищают от атак типа "Slowloris", когда злоумышленник устанавливает множество медленных соединений, исчерпывая ресурсы сервера. В http.Server важно настраивать:
ReadTimeout: Максимальное время на чтение всего запроса, включая тело.WriteTimeout: Максимальное время на запись ответа.IdleTimeout: Время, в течение которого открытое keep-alive соединение может простаивать.
srv := &http.Server{
Addr: ":8080",
ReadTimeout: 5 * time.Second,
WriteTimeout: 10 * time.Second,
IdleTimeout: 60 * time.Second,
}
3. Ограничение количества одновременных соединений
Можно ограничить общее число активных соединений с помощью семафора (реализованного на буферизованном канале), чтобы предотвратить исчерпание файловых дескрипторов.
// Ограничиваем до 100 одновременных обработок
var connLimiter = make(chan struct{}, 100)
http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
connLimiter <- struct{}{}
defer func() { <-connLimiter }()
// ... обработка запроса
})
Внешние уровни защиты
Важно помнить, что защита на уровне приложения — это лишь один из рубежей. Для надежной защиты необходимы:
- Reverse Proxy (Nginx, HAProxy): Могут выполнять rate limiting и кэширование до того, как запрос дойдет до Go-приложения.
- Firewall (iptables, nftables): Блокировка трафика с подозрительных IP на уровне ОС.
- Специализированные сервисы (Cloudflare, Akamai): Предоставляют глобальную сеть для фильтрации вредоносного трафика еще до того, как он достигнет вашей инфраструктуры. Это самый эффективный метод против масштабных DDoS-атак.
Ответ 18+ 🔞
Ну слушай, вот сидишь ты, пишешь своё приложение на Go, всё летает, красота. А потом — бац! — какой-то пидарас шерстяной с ботнетом из тостеров решает тебе жизнь устроить, и начинается этот пиздец под названием DDoS.
Конечно, если тебя атакуют по-взрослому, овердохуища гигабит в секунду, то тут одной программной защитой не отделаешься — нужны облачные щиты, фаерволы на уровне сети, вся эта ебопота. Но на уровне самого приложения можно сделать несколько простых, но ёмких вещей, чтобы мелкого распиздяя, который решил потренироваться, послать нахуй с минимальными потерями.
1. Rate Limiting, или «Не гони, мудак!»
Самая простая и действенная штука — ограничить, сколько запросов один клиент может слать за время. Представь, как к тебе в гости приходит один знакомый и начинает со скоростью пулемёта рассказывать анекдоты. Сначала смешно, а через минуту уже хочется вилкой ему в глаз. Вот лимитер — это ты, который говорит: «Бля, давай по одному в секунду, а то я с ума сойду».
В Go для этого есть отличный пакет golang.org/x/time/rate. Берёшь и настраиваешь, сколько «анекдотов в секунду» ты готов слушать.
import "golang.org/x/time/rate"
// Допустим, 10 запросов в секунду, с возможным «запасом» в 20
limiter := rate.NewLimiter(10, 20)
http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if !limiter.Allow() {
http.Error(w, http.StatusText(http.StatusTooManyRequests), http.StatusTooManyRequests)
return
}
// ... а вот тут уже обрабатываем, если клиент не слишком распизделся
})
Важный момент, чувак: этот лимитер нужно привязывать к IP-адресу клиента, а не делать один на всех. Иначе один еблан заспамит, а нормальные пользователи будут страдать. Это как если бы в баре один алкаш выпил всё пиво, а остальным бы сказали: «Извините, лимит исчерпан».
2. Таймауты, или «Давай быстрее, я жду!»
Есть такая хитрая жопа атака — Slowloris. Злоумышленник открывает кучу соединений и начинает оооочень медленно шептать тебе на ухо свой запрос, по байту в минуту. Сервер ждёт-ждёт, а соединения все заняты, новые принять не может — и накрылся медным тазом.
Чтобы такого не было, нужно ставить жёсткие таймауты в самом http.Server. Сказать ему: «Слушай, если клиент за 5 секунд не прислал запрос — посылай его нахуй и освобождай сокет».
srv := &http.Server{
Addr: ":8080",
ReadTimeout: 5 * time.Second, // Время на прочтение всего запроса
WriteTimeout: 10 * time.Second, // Время на отправку ответа
IdleTimeout: 60 * time.Second, // Время, которое пустое соединение может висеть
}
Вот так. Поставил — и уже спокойнее спишь.
3. Ограничение одновременных соединений, или «Мест нет!»
Представь свою программу как маленький бар. У тебя есть, допустим, 100 стульев. Если приходит 101-й посетитель, он просто ждёт у входа, пока кто-то не уйдёт. Так и тут: можно ограничить общее число горутин, которые одновременно обрабатывают запросы. Реализуется это проще пареной репы — на буферизованном канале.
// Делаем «бар на 100 мест»
var connLimiter = make(chan struct{}, 100)
http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Пытаемся занять место
connLimiter <- struct{}{}
// Не забываем освободить, когда уходим!
defer func() { <-connLimiter }()
// ... а вот тут уже сама обработка запроса
})
Если все 100 мест заняты, 101-й запрос будет ждать в этой строчке connLimiter <- struct{}{}, пока не освободится слот. Это не даст твоему серверу сожрать всю память и CPU, когда на него навалятся.
Но это всё, блядь, цветочки!
Запомни главное: защита в самом приложении — это как поставить хороший замок на дверь квартиры. От вора-одиночки спасёт, но если приедет танк или орда мародёров — пиши пропало.
Поэтому для серьёзной защиты нужны внешние рубежи:
- Reverse Proxy (Nginx, HAProxy): Это такой здоровенный вышибала перед твоим баром. Он может сам отсеивать спам-запросы, кешировать статику и вообще принимать первый удар на себя.
- Фаервол (iptables): Это уже уровень операционки. Можно тупо дропать пакеты с IP-адресов, которые ведут себя как последние мудя.
- Специализированные сервисы (Cloudflare и прочие): Это, блядь, уже целая армия. Они стоят между тобой и всем интернетом, фильтруют трафик через свои дата-центры по всему миру и пропускают к тебе только чистых, проверенных ребят. От большого DDoS — это часто единственное спасение.
Короче, суть в чём: в своём коде делай лимитеры и таймауты — это обязательно. Но не питай иллюзий, что этого хватит на всё. Самые жирные атаки отбиваются на уровне инфраструктуры, до твоего кода дело даже не должно дойти. Вот так вот, ебать.