Cómo encontré una vulnerabilidad crítica de JWT en una app de préstamos
Durante un pentest a una fintech mexicana encontré que era posible manipular el token JWT para acceder a préstamos de otros usuarios. Así fue el proceso.
Contexto del engagement
Durante una evaluación de seguridad a una plataforma de préstamos personales (datos anonimizados), el alcance incluía la API REST que maneja el flujo completo: solicitud de crédito, aprobación, consulta de saldo y descarga de estados de cuenta.
El objetivo era evaluar el flujo de autenticación y los controles de acceso sobre los recursos financieros de los usuarios.
Reconocimiento inicial
Lo primero fue mapear todos los endpoints disponibles usando Burp Suite con el proxy configurado sobre el dispositivo de prueba.
Al interceptar el flujo de login, el servidor devolvía un token JWT con la siguiente estructura en el payload:
{
"sub": "user_id_12345",
"loan_id": "loan_98765",
"role": "borrower",
"exp": 1710000000
}
El campo loan_id dentro del token llamó la atención inmediatamente. ¿Por qué el identificador del préstamo estaba embebido en el JWT?
Explotación
La hipótesis era que la API usaba el loan_id del token para autorizar el acceso a los recursos del préstamo, en lugar de validarlo contra el usuario autenticado.
Para probar esto, modifiqué el payload del JWT cambiando el loan_id por uno perteneciente a otro usuario. Como la firma del token no se estaba validando correctamente en el servidor, la petición fue aceptada.
GET /api/v2/loans/details HTTP/1.1
Host: api.fintech-redacted.mx
Authorization: Bearer [JWT_MODIFICADO]
La respuesta incluía todos los datos del préstamo del usuario víctima: monto, saldo pendiente, historial de pagos y datos personales.
Impacto
Esta vulnerabilidad combina dos problemas: Broken Object Level Authorization (BOLA) y validación incorrecta de firma JWT.
Cualquier usuario autenticado podía acceder a información financiera de todos los demás usuarios de la plataforma con solo modificar el loan_id en su propio token.
El impacto directo incluye exposición de datos financieros personales y violación de regulaciones de privacidad aplicables al sector financiero en México.
Remediación recomendada
La corrección requiere dos cambios:
Primero, validar siempre la firma del JWT en el servidor usando una clave secreta robusta. No asumir que el token es confiable sin verificarlo.
Segundo, nunca usar el loan_id del token como fuente de autorización. El servidor debe obtener los préstamos del usuario consultando la base de datos usando el sub (user_id) y verificar que el recurso solicitado pertenece a ese usuario.
# Incorrecto
loan_id = jwt_payload['loan_id']
loan = db.get_loan(loan_id)
# Correcto
user_id = jwt_payload['sub']
loan = db.get_loan_by_user(user_id)
if not loan:
return 403
Conclusión
Este tipo de vulnerabilidad es más común de lo que parece en fintechs que desarrollan rápido. La presión por lanzar features hace que los controles de autorización queden como deuda técnica.
Una evaluación de seguridad antes del lanzamiento a producción hubiera detectado esto antes de que cualquier usuario real pudiera explotarlo.
¿Tu plataforma maneja recursos financieros por API? Contáctanos para una evaluación.