jwtSecret: process.env.JWT_SECRET || 'leadflow-super-secret-2024'
jwt.sign() en src/auth/jwt.js:6.
Si JWT_SECRET no está definida (ausente en docker-compose.yml), la app arranca con el valor hardcodeado.
docker-compose.yml NO define JWT_SECRET. El defecto es activo en prod.
undefined (fail-secure: crash si no está)openssl rand -hex 32JWT_SECRET a docker-compose.yml como env obligatoriaif (process.env.DEBUG === 'true' || process.env.NODE_ENV !== 'production') { req.user = { id: 0, role: 'admin' }; return next(); // bypass total }
docker-compose.yml define DEBUG=true en el servicio api con NODE_ENV=production.
El bypass es activo en producción: cualquier petición sin token recibe rol admin.
DEBUG=true del docker-compose.yml de prod// config/index.js dbPassword: process.env.DB_PASSWORD || 'postgres' // docker-compose.yml POSTGRES_PASSWORD: postgres POSTGRES_USER: postgres
openssl rand -base64 32const cipher = crypto.createCipheriv( 'aes-128-ecb', // ECB no usa IV → mismo input = mismo output config.encryptionKey.slice(0, 16), null );
encryptionKey: process.env.ENCRYPTION_KEY
|| 'default-encryption-key-32chars!!'
aes-256-gcm con IV aleatorio por operaciónENCRYPTION_KEYfunction hashPassword(password) {
return crypto.createHash('md5')
.update(password + 'leadflow') // salt estático = inútil
.digest('hex');
}
bcrypt (cost 12+) o argon2idcorsOrigin: process.env.CORS_ORIGIN || '*'
https://app.leadflow.io* con credenciales (credentials: true)rateLimitDisabled: process.env.RATE_LIMIT_DISABLED || 'true' // Fail-open: si no se define la var, el rate limit está OFF
/login, /reset-password y cualquier endpoint público.RATE_LIMIT_DISABLED || 'false'ssl: process.env.DB_SSL === 'true'
? { rejectUnauthorized: false } // MitM posible
: false // sin TLS por defecto
rejectUnauthorized: false permite ataques Man-in-the-Middle.DB_SSL=true en producciónrejectUnauthorized: true con certificado CA válidojwtExpiry: process.env.JWT_EXPIRY || '7d'
7d es conservador y aceptable en ausencia de requisitos estrictos. No constituye una vulnerabilidad. Considerar reducir a 1h con refresh tokens para mayor seguridad, pero no es bloqueante.
| ID | Hallazgo | Prioridad | Esfuerzo | Acción inmediata |
|---|---|---|---|---|
| F-02 | Bypass auth en DEBUG | P0 — Bloquear deploy | Bajo | Eliminar bloque debug de auth.js + quitar DEBUG=true de compose |
| F-01 | JWT Secret fallback | P0 — Bloquear deploy | Bajo | Eliminar fallback, generar secret, añadir a compose |
| F-05 | Hash MD5 contraseñas | P0 — Bloquear deploy | Medio | Reemplazar por bcrypt/argon2id; migrar en login |
| F-03 | Credenciales DB por defecto | P0 — Bloquear deploy | Bajo | Rotar passwords, inyectar via secrets |
| F-04 | AES-ECB + key fallback | P0 — Bloquear deploy | Alto | Migrar a AES-256-GCM; re-cifrar datos existentes |
| F-06 | CORS wildcard | P1 — Sprint actual | Bajo | Lista blanca de orígenes explícita |
| F-07 | Rate limit desactivado | P1 — Sprint actual | Bajo | Invertir defecto a 'false' |
| F-08 | SSL desactivado en DB | P2 — Próximo sprint | Medio | Activar TLS + cert CA válido |