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

171 lines
5.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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)
```typescript
} 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)
```typescript
} 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
```typescript
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
```typescript
// 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
```typescript
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
```bash
# 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):
```bash
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.