Files
math2-platform/backend/SECURITY_CHANGELOG.md
Renato bc43c9e772
Some checks failed
Test Suite / test-backend (push) Has been cancelled
Test Suite / test-frontend (push) Has been cancelled
Test Suite / e2e-tests (push) Has been cancelled
Test Suite / coverage-check (push) Has been cancelled
🎓 Initial commit: Math2 Platform - Plataforma de Álgebra Lineal PRO
 Características:
- 45 ejercicios universitarios (Basic → Advanced)
- Renderizado LaTeX profesional
- IA generativa (Z.ai/DashScope)
- Docker 9 servicios
- Tests 123/123 pasando
- Seguridad enterprise (JWT, XSS, Rate limiting)

🐳 Infraestructura:
- Next.js 14 + Node.js 20
- PostgreSQL 15 + Redis 7
- Docker Compose completo
- Nginx + SSL ready

📚 Documentación:
- 5 informes técnicos completos
- README profesional
- Scripts de deployment automatizados

Estado: Producción lista 
2026-03-31 11:27:11 -03:00

5.2 KiB
Raw Permalink Blame History

Documentación de Cambio de Seguridad - Token Blacklist

Resumen

Se corrigió un FAIL-OPEN CRÍTICO en la verificación de token blacklist que permitía el bypass de autenticación cuando Redis estaba indisponible.

Fecha del Cambio

2024-03-30

Archivo Modificado

backend/src/shared/database/redis.client.ts

Problema Original (Líneas 145-157)

} catch (error) {
  logger.error({ error }, 'Redis unavailable - cannot check token blacklist');
  // Return false to allow request to proceed (fail-open for availability)
  // Note: This is a security trade-off. If strict security is required,
  // change to return true (fail-closed)
  return false;  // <-- BYPASS DE SEGURIDAD CRÍTICO
}

Riesgo: Cuando Redis fallaba, cualquier token (incluso los blacklisteados) se consideraba válido, permitiendo autenticación no autorizada.

Solución Implementada

1. Comportamiento FAIL-CLOSED (Seguridad Máxima)

} catch (error) {
  logger.error({ 
    error: (error as Error).message,
    consecutiveFailures,
    tokenPrefix: token.substring(0, 10),
    timestamp: new Date().toISOString(),
    service: 'token-blacklist',
    circuitBreakerOpen: false
  }, 'Redis unavailable - SECURITY: blocking token');
  
  throw new AuthenticationError('Unable to verify token status. Service temporarily unavailable.');
}

2. Circuit Breaker con Fallback

  • Umbral: 5 fallos consecutivos activan el circuit breaker
  • Fallback: Cache en memoria (TTL 1 minuto) para operaciones de blacklist
  • Hashing: SHA256 para almacenamiento seguro (nunca guarda tokens completos)

3. Retry con Backoff Exponencial

  • Intentos: 3 reintentos máximo
  • Delay: 100ms × intento (100ms, 200ms, 300ms)

4. Métricas de Monitoreo

const metrics = {
  redisBlacklistFailures: 0,           // Fallos totales
  redisBlacklistConsecutiveFailures: 0, // Fallos consecutivos actuales
  redisBlacklistSuccesses: 0,          // Éxitos totales
  circuitBreakerOpens: 0              // Veces que se abrió el circuit breaker
};

5. Logging Estructurado

  • Nunca se loguean tokens completos (solo prefijo de 10 caracteres)
  • Timestamp ISO8601
  • Identificación de servicio
  • Contadores de fallos consecutivos

Cambios de Comportamiento

Escenario Antes (FAIL-OPEN) Después (FAIL-CLOSED)
Redis indisponible Token permitido Error 401 lanzado
Error temporal Token permitido Retry con backoff
5+ fallos consecutivos Token permitido Circuit breaker activado
Verificación exitosa Token rechazado/aceptado Sin cambios

Impacto en Disponibilidad

⚠️ IMPORTANTE: Este cambio puede causar downtime del servicio de autenticación si Redis falla.

Recomendaciones para Alta Disponibilidad

  1. Implementar Redis Replica: Configurar Redis Cluster o Sentinel
  2. Monitoreo: Alertas inmediatas cuando metrics.redisBlacklistFailures > 0
  3. Health Checks: Verificar estado de Redis antes de despliegues
  4. Graceful Degradation: Cache en memoria como último recurso

API Changes

Nuevas Funciones Exportadas

// Obtener métricas actuales
export function getBlacklistMetrics(): {
  redisBlacklistFailures: number;
  redisBlacklistConsecutiveFailures: number;
  redisBlacklistSuccesses: number;
  circuitBreakerOpens: number;
}

// Resetear métricas (útil para testing)
export function resetBlacklistMetrics(): void

Error Handling

class AuthenticationError extends Error {
  message: 'Unable to verify token status. Service temporarily unavailable.'
}

Tests Unitarios

Archivo: backend/tests/redis.client.test.ts

14 tests implementados:

  1. Verificar token blacklisteado
  2. Verificar token no blacklisteado
  3. FAIL-CLOSED cuando Redis falla
  4. FAIL-CLOSED con múltiples fallos
  5. Retry con backoff exponencial
  6. Métricas de éxito
  7. Blacklist exitoso en Redis
  8. Fallback a cache en memoria
  9. Circuit breaker se abre después de 5 fallos
  10. Reset de contador tras éxito
  11. Nunca permite acceso con Redis caído
  12. No loguea tokens completos
  13. Tracking de métricas
  14. Reset de métricas

Comandos de Verificación

# TypeScript
npm run type-check

# Tests
npm test -- tests/redis.client.test.ts

# Build
npm run build

Lista de Verificación Pre-Deploy

  • Redis Cluster configurado en producción
  • Alertas de métricas configuradas
  • Documentación de rollback lista
  • Comunicación al equipo sobre cambio de comportamiento
  • Monitoreo de errores 401 post-deploy
  • Plan de contingencia si hay degradación de servicio

Rollback

Si es necesario revertir (aunque NO recomendado por riesgo de seguridad):

git revert <commit-hash>
# O manualmente: restaurar return false en catch block

Referencias

  • Issue: Token blacklist bypass (fixear.md Issue #2)
  • Archivo: backend/src/shared/database/redis.client.ts:145-157
  • PR: N/A (cambio directo)

Nota de Seguridad: Este cambio es CRÍTICO y bloquea un vector de ataque de autenticación. No debe revertirse sin considerar las implicaciones de seguridad.