Контекст: В backend/internal/services/auth.go реализована JWT-аутентификация с access (15 мин) и refresh (30 дней) токенами. Текущая реализация имеет критические уязвимости.
Refresh токены не сохраняются в БД — они валидируются только по криптографической подписи. Это значит:
// 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 // Только проверка подписи!
})
// ... нет проверки в БД, нет отзыва
}
Хранить хеш refresh токена в таблице refresh_tokens (уже есть в миграции 000002_create_users.up.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()
);
SHA256(token) в БД → отдаём пользователю plain tokenexpires_at → удаляем старый, создаём новый (ротация)| Подход | Плюсы | Минусы |
|---|---|---|
| БД + ротация (рекомендую) | Полный контроль, отзыв, безопасность | Немного сложнее, лишний round-trip к БД |
| JWT blacklist в Redis | Быстро, не трогает БД | Нужно хранить до expiry, нет ротации |
| Короткий access + долгий refresh без БД | Просто | Небезопасно — текущее состояние |
Задача: Code review PhotoPlaces — выявление критических уязвимостей безопасности
#backend #security #jwt #auth #critical #best-practice