FiberMiddleTechnical

Как валидировать и ограничивать размер request body, чтобы не получить DoS?

BodyLimit в fiber.Config ограничивает размер тела глобально; для стриминга добавляйте io.LimitReader явно. Rate-limiting middleware предотвращает DoS на уровне числа запросов.

Глобальный лимит тела через fiber.Config

Самый простой способ защититься от DoS с огромным телом — установить BodyLimit при создании приложения. Fiber возвращает 413 Request Entity Too Large до того, как тело попадает в хендлер:

app := fiber.New(fiber.Config{
	BodyLimit: 1 * 1024 * 1024, // 1 MB по умолчанию
})

Переопределение лимита для конкретного маршрута

Иногда один эндпоинт (upload) требует большего лимита, а остальные — меньшего:

// Глобально: 1 MB
app := fiber.New(fiber.Config{
	BodyLimit: 1 * 1024 * 1024,
})

// Для upload: переопределяем через RequestCtx
app.Post("/upload", func(c *fiber.Ctx) error {
	// Увеличиваем лимит только для этого запроса
	c.Request().SetMaxBodySize(50 * 1024 * 1024)

	file, err := c.FormFile("file")
	if err != nil {
		return c.Status(fiber.StatusBadRequest).
			JSON(fiber.Map{"error": "invalid file"})
	}
	if file.Size > 10*1024*1024 {
		return c.Status(fiber.StatusRequestEntityTooLarge).
			JSON(fiber.Map{"error": "file too large"})
	}
	return c.SaveFile(file, "./uploads/"+file.Filename)
})

Валидация Content-Length заранее

app.Use(func(c *fiber.Ctx) error {
	const maxSize = 1 * 1024 * 1024 // 1 MB
	if c.Request().Header.ContentLength() > maxSize {
		return c.Status(fiber.StatusRequestEntityTooLarge).
			JSON(fiber.Map{"error": "request too large"})
	}
	return c.Next()
})

Rate-limiting для защиты от DoS

import "github.com/gofiber/fiber/v2/middleware/limiter"

// Глобальный rate-limit: 100 запросов/минуту на IP
app.Use(limiter.New(limiter.Config{
	Max:        100,
	Expiration: 1 * time.Minute,
	KeyGenerator: func(c *fiber.Ctx) string {
		return c.IP() // используем реальный IP (с TrustedProxies)
	},
	LimitReached: func(c *fiber.Ctx) error {
		return c.Status(fiber.StatusTooManyRequests).
			JSON(fiber.Map{"error": "rate limit exceeded"})
	},
}))

// Более жёсткий лимит на auth эндпоинты
app.Post("/auth/login", limiter.New(limiter.Config{
	Max:        5,
	Expiration: 1 * time.Minute,
}), loginHandler)

Защита от slow-body атак (ReadTimeout)

app := fiber.New(fiber.Config{
	BodyLimit:   1 * 1024 * 1024,
	ReadTimeout: 5 * time.Second, // клиент должен отправить тело за 5 сек
})

Подводные камни

  • BodyLimit не работает для chunked encoding без Content-Length: если клиент не посылает заголовок Content-Length, BodyLimit срабатывает только при достижении лимита при чтении — добавьте ReadTimeout как второй рубеж.
  • SetMaxBodySize в хендлере слишком поздно: вызов после частичного чтения тела не имеет эффекта; вызывайте до любого чтения.
  • Rate-limit по IP без TrustedProxies: без корректной настройки все запросы через балансировщик имеют один IP — rate-limit работает как глобальный, а не per-client.
  • Отсутствие лимита на количество полей формы: multipart/form-data с миллионами полей вызывает OOM; ограничивайте количество частей.
  • В памяти vs. диск для больших файлов: Fiber/Fasthttp буферизует тело в памяти; для файлов > 10 MB используйте стриминг напрямую в хранилище.
  • Content-Type без валидации: злоумышленник может послать JSON-тело с Content-Type: multipart/form-data; проверяйте Content-Type явно в middleware.

Common mistakes

  • Отвечать определением без production-сценария.
  • Не называть runtime boundary, security boundary или failure mode.
  • Игнорировать версию API, observability и тестовую проверку.

What the interviewer is testing

  • Объясняет механизм своими словами и без выдуманных API.
  • Называет реальные риски, диагностику и критерий корректности.
  • Связывает ответ с текущей документацией и миграционными ограничениями.

Sources

Related topics