# Backend: Безопасность JWT и Refresh Tokens **Контекст**: В `backend/internal/services/auth.go` реализована JWT-аутентификация с access (15 мин) и refresh (30 дней) токенами. Текущая реализация имеет критические уязвимости. ## Суть **Refresh токены не сохраняются в БД** — они валидируются только по криптографической подписи. Это значит: 1. Нет возможности отозвать токен (logout, смена пароля, бан пользователя) 2. Нет ротации refresh токенов (уязвимость к replay атакам) 3. Утечка refresh токена = полный доступ на 30 дней 4. Нельзя ограничить количество активных сессий пользователя ## Код (проблемное место) ```go // backend/internal/services/auth.go:152-164 func (s *AuthService) ValidateRefreshToken(tokenString string) (string, error) { token, err := jwt.ParseWithClaims(tokenString, &jwt.RegisteredClaims{}, func(t *jwt.Token) (interface{}, error) { return s.refreshSecret, nil // Только проверка подписи! }) // ... нет проверки в БД, нет отзыва } ``` ## Решение (Best Practice) Хранить хеш refresh токена в таблице `refresh_tokens` (уже есть в миграции `000002_create_users.up.sql`): ```sql CREATE TABLE refresh_tokens ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), user_id UUID NOT NULL REFERENCES users(id), token_hash VARCHAR(255) NOT NULL, -- SHA-256 от токена expires_at TIMESTAMPTZ NOT NULL, created_at TIMESTAMPTZ NOT NULL DEFAULT now() ); ``` ### Алгоритм: 1. При логине: генерируем refresh token → сохраняем `SHA256(token)` в БД → отдаём пользователю plain token 2. При refresh: хешируем полученный токен → ищем в БД → проверяем `expires_at` → **удаляем старый, создаём новый** (ротация) 3. При logout / смене пароля / бане: удаляем все токены пользователя из БД ## Альтернативы | Подход | Плюсы | Минусы | |--------|-------|--------| | **БД + ротация (рекомендую)** | Полный контроль, отзыв, безопасность | Немного сложнее, лишний round-trip к БД | | **JWT blacklist в Redis** | Быстро, не трогает БД | Нужно хранить до expiry, нет ротации | | **Короткий access + долгий refresh без БД** | Просто | **Небезопасно** — текущее состояние | ## Связанные заметки - [[decision-jwt-refresh-storage]] - [[backend-rate-limiting]] - [[architecture-overview]] ## Источник Задача: Code review PhotoPlaces — выявление критических уязвимостей безопасности ## Теги #backend #security #jwt #auth #critical #best-practice