🗺
Fases de Auditoría Completadas
Reconocimiento
Completado
Análisis Esquema
Completado
Autorización
3 Hallazgos
Inyección
2 Hallazgos
DoS / Complejidad
4 Hallazgos
Lógica Negocio
Completado
Subscripciones
2 Hallazgos
🔴
Hallazgos Críticos y Altos
CRÍTICO
GQFL-001
Introspección habilitada en producción — esquema completo expuesto
El endpoint
https://api.flowdesk.io/graphql responde a queries de introspección completa, exponiendo 247 tipos, 89 mutations (incluyendo mutaciones de gestión de roles y facturación) y argumentos con nombres reveladores como adminOverride, internalUserId.
# Query de introspección enviada — respuesta en <300ms
query {
__schema {
types {
name
fields {
name
type { name kind }
}
}
}
}
# Extracto de la respuesta — tipos sensibles encontrados:
# InternalUserAdmin, BillingOverrideInput, SuperAdminConfig,
# HiddenFeatureFlag, LegacyMigrationResolver...
Impacto Directo
El atacante obtiene el mapa completo de la API: todos los tipos, mutations de admin, relaciones entre entidades y estructura de IDs internos.
Superficie Expuesta
247 tipos GraphQL, 89 mutations, 34 queries con argumentos tipo adminOverride: Boolean documentados en el esquema.
✓ Remediación
- Deshabilitar introspección en NODE_ENV=production en Apollo Server 3.x (
introspection: process.env.NODE_ENV !== 'production') - Implementar reglas de validación en Graphql Armor para bloquear queries con profundidad excesiva de introspección
- Mantener documentación interna de la API con GraphQL Voyager en entorno privado (VPN)
CRÍTICO
GQFL-002
IDOR + Escalada de privilegios en mutation updateUserRole
La mutation
updateUserRole acepta un userId arbitrario sin verificar que pertenece a la organización del usuario autenticado. Un usuario con rol member puede ascender a otro usuario a admin o incluso superadmin si conoce (o enumera) su ID.
# Ejecutado con token JWT de cuenta "member" (plan Starter)
mutation EscaladaPrivilegios {
updateUserRole(
userId: "VXNlcjoxMjM0" # base64("User:1234") — ID de otro tenant
role: SUPERADMIN
organizationId: "T3JnOjU2Nzg="
) {
user { id role email }
success
}
}
# Respuesta: { "data": { "updateUserRole": { "user": { "role": "SUPERADMIN" }, "success": true } } }
# ⚠ Sin error. Sin validación de pertenencia a organización.
Impacto
Control total de cualquier cuenta FlowDesk. Acceso a datos de todos los tenants, gestión de facturación, exportación de CRM completo.
Explotabilidad
Trivial. IDs son Relay Global IDs en base64 (enumerables). No requiere herramientas especiales, solo una cuenta member válida.
✓ Remediación
- En el resolver: verificar que
userIdpertenece a la organización del usuario JWT antes de ejecutar la mutation - Implementar directiva @auth con verificación de ownership:
@authorized(requires: ADMIN, scope: ORGANIZATION) - Añadir test de integración que verifique cross-tenant isolation en todas las mutations de gestión de usuarios
CRÍTICO
GQFL-003
Hasura — Header injection x-hasura-role permite bypass de permisos
El endpoint de reportes Hasura (
https://reports.flowdesk.io/v1/graphql) acepta y confía en el header x-hasura-role enviado directamente por el cliente, sin validación contra el JWT. Esto permite inyectar el rol admin o superadmin en cualquier sesión autenticada.
# Request HTTP — usuario autenticado como "viewer"
POST https://reports.flowdesk.io/v1/graphql
Authorization: Bearer eyJhbGciOiJIUzI1NiJ9... # Token viewer legítimo
x-hasura-role: admin # Header inyectado manualmente
x-hasura-org-id: 5678 # Cualquier organización
Content-Type: application/json
query {
billing_records(limit: 1000) {
id amount customer_email card_last4 plan
}
}
# Respuesta: 1.000 registros de facturación de todos los tenants ✓
✓ Remediación
- Configurar Hasura para leer el rol exclusivamente desde el JWT claim (
x-hasura-allowed-rolesen el token), nunca de headers externos - Habilitar
HASURA_GRAPHQL_UNAUTHORIZED_ROLEy eliminar permisos de bypass en JWT webhook - Añadir WAF rule para bloquear requests con headers
x-hasura-*provenientes de clientes externos
Alto
GQFL-004
DoS por query batching sin límite — servidor inutilizable en 8s
El servidor Apollo acepta arrays de operaciones en una sola request HTTP sin límite configurado. Enviando un array de 500 operaciones costosas con profundidad 12, se provocó un tiempo de respuesta de 47s y consumo de CPU al 100% durante 8s, haciendo el servicio inutilizable para el resto de usuarios.
# 500 operaciones en un único POST — sin autenticación especial
[
{ "query": "{ projects { tasks { comments { author { projects { tasks { id } } } } } } }" },
{ "query": "{ projects { tasks { comments { author { projects { tasks { id } } } } } } }" },
// ... x500
]
# Resultado: Response time p99: 47.3s | CPU: 100% (8s) | Servicio degradado
✓ Remediación
- Limitar batch a máximo 10 operaciones por request (
allowBatchedHttpRequests: falseo plugin custom) - Implementar GraphQL Armor con
maxDepth: 7ymaxAliases: 15 - Rate limiting a nivel de operación por usuario: máx. 100 req/min en Apollo Server
Alto
GQFL-005
WebSocket subscriptions — token JWT no revalidado tras expiración
Las subscriptions GraphQL sobre WebSocket (
wss://api.flowdesk.io/graphql) autentican al cliente en el mensaje connection_init pero no re-validan el token en mensajes posteriores. Un JWT revocado o expirado sigue recibiendo eventos durante horas.
# 1. Conectar con token válido
connection_init: { "authToken": "eyJ...valid" }
# 2. Revocar el token en el servidor (logout, ban de cuenta)
DELETE /api/auth/sessions/abc123
# 3. La subscripción WS continúa recibiendo datos 3h 47m después
subscription {
projectUpdated(projectId: "abc") {
title tasks { assignee { email } }
}
}
# ← Sigue recibiendo updates en tiempo real tras revocación ✓
✓ Remediación
- Implementar middleware
onMessageque re-valide el JWT en cada mensaje WS o cada N segundos - Publicar evento de revocación en Redis y verificar en el servidor WS antes de enviar cada update
- Definir TTL máximo de conexión WS igual al tiempo de expiración del JWT (típicamente 15min)
🟠
Hallazgos Medios y Bajos
Medio
GQFL-006
Field suggestions revelan esquema privado (introspección deshabilitable pero field hints activos)
Medio
GQFL-007
NoSQL injection en resolver searchProjects vía argumento filter.name
Medio
GQFL-008
Directive flooding — 10.000 directivas @include colapsan el parser (CVE-2024-47614 pattern)
Medio
GQFL-009
Upload mutation — sin validación de content-type real (magic bytes); path traversal posible
Bajo
GQFL-010
Relay Global IDs predecibles — enumeración trivial de entidades internas
Bajo
GQFL-011
Ausencia de CSRF protection en endpoint GraphQL con sesiones por cookie
⛓
Cadenas de Ataque de Mayor Impacto
Cadena Crítica
Acceso completo a datos de todos los tenants
GQFL-001
Introspección
Introspección
→
GQFL-010
Enum. IDs
Enum. IDs
→
GQFL-002
IDOR + Rol
IDOR + Rol
→
GQFL-003
Hasura Admin
Hasura Admin
→
Full DB Access
12.000 tenants
12.000 tenants
Impacto final: Un atacante con una cuenta member legítima puede obtener acceso de superadmin y volcar todos los datos de clientes, incluyendo contratos, CRM completo y registros de facturación de los 12.000 usuarios activos de FlowDesk Enterprise. Tiempo estimado de explotación: ~15 minutos.
Cadena Alta
DoS dirigido + extracción de datos durante incidente
GQFL-004
Batch DoS
Batch DoS
→
GQFL-008
Directive Flood
Directive Flood
→
Servicio degradado
SLA breach
SLA breach
Impacto final: Degradación del servicio durante ventanas críticas (fin de mes, cierres de sprint), posible breach del SLA Enterprise. Costo estimado: penalizaciones contractuales por disponibilidad + reputación.
🛡
Hoja de Ruta de Remediación
| ID | Hallazgo | Prioridad | Esfuerzo | Equipo | Deadline Sugerido |
|---|---|---|---|---|---|
GQFL-003 |
Hasura header injection | Inmediato | DevOps / Backend | Hoy | |
GQFL-002 |
IDOR + Escalada de privilegios | Inmediato | Backend | 24h | |
GQFL-001 |
Deshabilitar introspección en prod | Esta semana | Backend | 16 Jun | |
GQFL-004 |
Rate limit en batch queries | Esta semana | Backend | 17 Jun | |
GQFL-005 |
Re-validación JWT en WebSocket | Esta semana | Backend / Infra | 20 Jun | |
GQFL-006..009 |
Field hints, NoSQLi, Directives, Upload | Este mes | Backend / Security | 30 Jun | |
GQFL-010..011 |
Relay IDs + CSRF hardening | Trimestre | Backend | 31 Jul |
🔧
Herramientas Utilizadas en la Auditoría
Reconocimiento
graphw00f · graphql-cop · Graphinder · clairvoyance
Explotación
inql (Burp) · GraphQLmap · CrackQL · BatchQL
Intercepción
Burp Suite Pro · OWASP ZAP · Insomnia · WebSocket King
Visualización
GraphQL Voyager · Nuclei GraphQL templates