From e21abb8f138a47a6661041d12751165c84494542 Mon Sep 17 00:00:00 2001 From: Repinoid Date: Sat, 10 Oct 2026 17:07:57 +0300 Subject: [PATCH] fix(security): JWT validate against prod gateway only + cap grace renewal - jwt: drop test/dev gateway from nubesAPIEndpoints (test-stand token was accepted as prod) - cache: grace bounded by graceDeadline from last successful validation (no more indefinite renewal) --- app/auth/cache.go | 23 +++++++++++++++-------- app/auth/jwt.go | 6 +++--- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/app/auth/cache.go b/app/auth/cache.go index ae6bd1e..5e27bdf 100644 --- a/app/auth/cache.go +++ b/app/auth/cache.go @@ -27,7 +27,8 @@ const cacheGraceTTL = 2 * time.Minute const cacheCleanupInterval = 5 * time.Minute type tokenCacheEntry struct { - validUntil time.Time + validUntil time.Time + graceDeadline time.Time // потолок grace от последней УСПЕШНОЙ проверки (не продлевается) } var ( @@ -66,18 +67,20 @@ func PingNubesAPICached(ctx context.Context, endpoint, token string) error { return err } if err != nil { - // Сетевая ошибка шлюза: если токен ранее был валиден — даём grace. - if ok { - tokenCacheMu.Lock() - tokenCache[key] = tokenCacheEntry{validUntil: time.Now().Add(cacheGraceTTL)} - tokenCacheMu.Unlock() + // Сетевая ошибка шлюза: если токен ранее был валиден — даём grace, но + // НЕ дальше graceDeadline. Раньше validUntil сдвигали вперёд на каждый + // сетевой сбой — отозванный токен жил бы вечно при повторных ошибках. + if ok && now.Before(entry.graceDeadline) { return nil } return err } tokenCacheMu.Lock() - tokenCache[key] = tokenCacheEntry{validUntil: time.Now().Add(cacheTTL)} + tokenCache[key] = tokenCacheEntry{ + validUntil: now.Add(cacheTTL), + graceDeadline: now.Add(cacheGraceTTL), + } tokenCacheMu.Unlock() return nil } @@ -85,8 +88,12 @@ func PingNubesAPICached(ctx context.Context, endpoint, token string) error { // WarmTokenCache — записывает токен в кэш как валидный без вызова шлюза. // Используется после успешной свежей проверки при логине (jwtAuth). func WarmTokenCache(token string) { + now := time.Now() tokenCacheMu.Lock() - tokenCache[tokenCacheKey(token)] = tokenCacheEntry{validUntil: time.Now().Add(cacheTTL)} + tokenCache[tokenCacheKey(token)] = tokenCacheEntry{ + validUntil: now.Add(cacheTTL), + graceDeadline: now.Add(cacheGraceTTL), + } tokenCacheMu.Unlock() } diff --git a/app/auth/jwt.go b/app/auth/jwt.go index 4459a68..c154572 100644 --- a/app/auth/jwt.go +++ b/app/auth/jwt.go @@ -71,11 +71,11 @@ var ErrTokenRejected = errors.New("token rejected by nubes API") // nubesAPIEndpoints — стенды Nubes API Gateway, по которым валидируется токен. // Токен принимается, если ХОТЯ БЫ ОДИН стенд его принял. -// Юзер может вводить токен от любого стенда — главное, что токен действительный. +// Безопасность: только prod-шлюз. Тестовый/dev-шлюзы принимают токены своих +// стендов, и при их наличии токен тестового стенда считался бы валидным на проде. +// Специфичный endpoint можно переопределить через NUBES_ENDPOINT. var nubesAPIEndpoints = []string{ "https://lk-api-gateway.ngcloud.ru/api/v1/svc", - "https://lk-api-gateway-test.ngcloud.ru/api/v1/svc", - "https://lk-api-gateway-dev.ngcloud.ru/api/v1/svc", } // nubesHTTPClient — общий клиент для валидации токенов: переиспользует