63 lines
6.1 KiB
Markdown
63 lines
6.1 KiB
Markdown
# Task 12: Rate limit до аутентификации Report
|
||
|
||
**Date:** 2026-08-21
|
||
**Base Commit SHA:** `906148813ee66f6f52d7c291f335c1ebdca2a057`
|
||
**Status:** COMPLETED / PASS
|
||
**Assigned Agent:** Antigravity (Implementation Orchestrator)
|
||
|
||
---
|
||
|
||
## 1. Executive Summary
|
||
|
||
Устранена уязвимость неограниченной нагрузки на Session Store / базу данных при неаутентифицированном флуде:
|
||
|
||
1. **Pre-Authentication Rate Limiter (`src/lib/rate-limiter.ts` & `src/lib/auth/auth-guard.ts`):**
|
||
- Экспортирован `preAuthRateLimiter` со скользящим окном 60 запросов / 60 секунд на клиентский IP (`resolveClientIp(req)`).
|
||
- В `requireAuthenticatedUser(req)` проверка лимитера вынесена **до** обращения к Session Store (`prisma.session`):
|
||
- Запросы без сессионной куки сразу проверяются через `preAuthRateLimiter.assertAllowed('pre-auth:' + clientIp)` без единого обращения к Session Store / БД (0 запросов в БД). При превышении лимита возвращается `429 RATE_LIMIT_EXCEEDED` вместо `401`.
|
||
- Запросы с недействительными/поддельными куками после проверки в Session Store списывают попытку из `preAuthRateLimiter`, ограничивая максимальное число недействительных запросов к БД числом 60 в минуту.
|
||
2. **Защита аутентифицированных пользователей на общем ключе (`direct-client` / NAT):**
|
||
- При наличии валидной сессионной куки запрос успешно аутентифицируется и **не попадает под штрафной лимит `pre-auth`**.
|
||
- Аутентифицированный пользователь подчиняется исключительно своему изолированному `user-scoped` лимиту (`sessionUser.id`), что полностью исключает возможность DoS легальных пользователей через анонимный флуд с того же IP / `direct-client`.
|
||
3. **Оптимизация в `POST /api/posts/preview` (`src/app/api/posts/preview/route.ts`):**
|
||
- Поиск сессии в `getSessionFromRequest` теперь вызывается только при наличии заголовка `Cookie: randomayzer_session=...`, исключая холостые вызовы при анонимных превью.
|
||
4. **Тестирование (`tests/pre-auth-rate-limit.test.ts`):**
|
||
- Добавлено 5 всесторонних тестов, проверяющих:
|
||
- 60 запросов без куки -> 61-й запрос возвращает `429` с кодом `RATE_LIMIT_EXCEEDED`, 0 вызовов `sessionStore.getSession`;
|
||
- 60 запросов с фейковыми куками -> ровно 60 вызовов `sessionStore.getSession`, затем блокировка `429`;
|
||
- Аутентифицированный пользователь на том же `direct-client` успешно выполняет запросы (`200 OK`) при исчерпанном анонимном лимите;
|
||
- Защита всех 8 защищенных эндпоинтов (`/api/giveaways`, `/api/giveaways/[id]`, `/api/giveaways/[id]/participants`, `/api/giveaways/[id]/draw`, `/api/giveaways/[id]/snapshot`, `/api/giveaways/[id]/unlock`);
|
||
- Сохранение изоляции user-scoped лимитов.
|
||
|
||
---
|
||
|
||
## 2. Архитектурные решения
|
||
|
||
### Почему `requireAuthenticatedUser` вместо `middleware.ts`?
|
||
1. **Совместимость с Next.js 16 и тестами:** В Next.js 16 middleware по умолчанию работает в Edge runtime, в то время как Vitest и интеграционные тесты запускают route handlers напрямую как функции. Размещение pre-auth guard внутри `requireAuthenticatedUser` гарантирует 100% покрытие во всех тестах, одинаковое поведение в dev/test/production и отсутствие накладных расходов на сериализацию между runtime-слоями.
|
||
2. **Детерминированность:** Все защищенные маршруты используют `requireAuthenticatedUser` или `requireGiveawayOwner` (который внутри вызывает `requireAuthenticatedUser`), что обеспечивает единую точку входа и невозможность обойти лимитер.
|
||
|
||
---
|
||
|
||
## 3. Modified & Created Files
|
||
|
||
| File | Status | Description |
|
||
|------|--------|-------------|
|
||
| `src/lib/rate-limiter.ts` | MODIFIED | Экспортирован `preAuthRateLimiter` (60 req / 60s). |
|
||
| `src/lib/auth/auth-guard.ts` | MODIFIED | Добавлена проверка `preAuthRateLimiter` до Session Store и для failed auth. |
|
||
| `src/app/api/posts/preview/route.ts` | MODIFIED | Пропуск `getSessionFromRequest` при отсутствии куки `SESSION_COOKIE_NAME`. |
|
||
| `tests/pre-auth-rate-limit.test.ts` | NEW | 5 тестов покрытия pre-auth лимитирования и защиты Session Store. |
|
||
| `tests/rate-limit-identity.test.ts` | MODIFIED | Сброс `preAuthRateLimiter` в `beforeEach`. |
|
||
|
||
---
|
||
|
||
## 4. Verification Evidence
|
||
|
||
```text
|
||
npx prisma generate -> EXIT 0 (Prisma Client v5.22.0)
|
||
npx tsc --noEmit -> EXIT 0 (0 ошибок типизации во всех 58 тестовых файлах и кодовой базе)
|
||
npm test -> EXIT 0 (58 сьютов, 338 тестов пройдены успешно без БД)
|
||
npm run lint -> EXIT 0 (0 ошибок, 6 warnings на no-img-element)
|
||
npm run build -> EXIT 0 (Все 17 маршрутов скомпилированы успешно в Next.js 16.3.2)
|
||
npm audit --omit=dev -> EXIT 0 (0 vulnerabilities)
|
||
```
|