GraphQL ha sustituido a REST en muchas APIs modernas porque permite al cliente describir exactamente qué datos necesita en una sola petición, evitando over fetching y simplificando el ciclo de desarrollo frontend. Esa misma flexibilidad introduce vectores de ataque específicos que escapan al pentester web tradicional acostumbrado al modelo verbo + recurso de REST. Cuando el cliente puede componer su propia query, declarar alias, anidar subselecciones y enviar mutaciones encadenadas, la superficie de ataque se amplía y los controles que funcionaban en REST dejan de aplicar de manera trivial.
Esta guía cubre cómo se enumera una API GraphQL, qué vulnerabilidades aparecen una y otra vez en auditorías reales, qué tooling usa el pentester ofensivo y qué defensas técnicas debe implementar un equipo de plataforma para reducir el riesgo sin romper la experiencia de desarrollo.
Lo esencial. GraphQL no es menos seguro que REST, pero exige controles distintos. Las tres familias de bug que más se repiten en auditoría son introspection activa en producción, denegación de servicio por queries anidadas o aliased y fallos de autorización a nivel resolver. Disable introspection, query complexity limiter, persisted queries y autorización por resolver son la base mínima para una API GraphQL pública.
Diferencia GraphQL vs REST en seguridad
Tres rasgos arquitectónicos de GraphQL cambian la forma de auditar y defender la API:
- Single endpoint. Todas las operaciones llegan a
/graphql(o ruta equivalente). Eso simplifica routing pero invalida cualquier WAF o rate limit configurado por path. Una regla que protege/api/usersno protegequery { users { ... } }porque para el WAF es siempre la misma URL. - Query language en el cliente. En REST el servidor decide qué devuelve cada endpoint. En GraphQL el cliente compone la query y el servidor resuelve. Eso significa que validar entrada ya no es "comprobar el body JSON", es "comprobar la forma del AST de la query".
- Batching y multi operación. Una sola petición HTTP puede contener múltiples queries y mutaciones. Rate limit por request HTTP es engañoso, lo relevante es cuántas operaciones efectivas se ejecutan.
- Status codes inconsistentes. GraphQL devuelve casi todo como
200 OKcon un campoerrorsen el body. Las herramientas que inferían fallo por código HTTP (escáneres clásicos, dashboards de SRE) pierden visibilidad y los errores autorización pasan desapercibidos en logs.
Discovery y enumeración
El primer paso de cualquier pentest GraphQL es localizar el endpoint y reconstruir el schema. Cuanto más completo sea el schema visible, más rápido avanza el atacante.
- Introspection query. GraphQL incluye un mecanismo nativo de introspección que devuelve el schema completo (tipos, campos, argumentos, directivas) si está habilitado. En entornos de desarrollo es lo esperado, pero la mayoría de auditorías encuentran introspection abierta también en staging y, con más frecuencia de la deseable, en producción.
- Field suggestions. Aunque el equipo desactive introspection, muchas implementaciones devuelven sugerencias del tipo
Did you mean "userId"?cuando el atacante prueba un nombre de campo aproximado. Iterando sobre diccionarios de campos comunes se reconstruye una parte significativa del schema. - GraphQL Voyager, Playground y Apollo Sandbox. Interfaces de exploración como GraphQL Playground, GraphiQL, Voyager o Apollo Sandbox aparecen expuestas en producción con sorprendente frecuencia. Cuando están accesibles ofrecen al atacante un IDE completo sobre la API.
- Common paths. Las rutas más habituales son
/graphql,/api/graphql,/v1/graphql,/query,/gqly, en stacks Hasura,/v1/graphqlcon consola en/console. Fuzzing dirigido encuentra el endpoint en minutos.
Vulnerabilidades GraphQL más comunes
Las siguientes familias aparecen en prácticamente todas las auditorías de APIs GraphQL maduras. No son exhaustivas pero cubren la mayoría del riesgo real.
- Introspection en producción. El schema completo entregado al atacante reduce drásticamente el tiempo de descubrimiento y revela campos administrativos, mutaciones internas o tipos legacy que ningún cliente legítimo usa pero siguen ahí.
- DoS via query depth. GraphQL permite anidar relaciones recursivamente. Si los tipos
UseryPostse referencian mutuamente, una query anidada cincuenta niveles dispara consultas SQL exponenciales o agota memoria del proceso. - DoS via aliasing. Los alias permiten pedir el mismo campo N veces dentro de la misma operación bajo nombres distintos. Una operación con doscientos alias del mismo resolver costoso multiplica por doscientos el coste sin levantar sospechas de rate limit por petición.
- DoS via batching. Implementaciones que soportan array batching aceptan múltiples operaciones en una sola petición HTTP. Combinado con aliasing y depth permite construir bombas de carga con un solo request.
- IDOR y BOLA en resolvers. El error más frecuente y de mayor impacto. La autorización se verifica en el gateway o en el wrapper de autenticación, pero el resolver de un tipo anidado no comprueba que el usuario actual tenga acceso al objeto referenciado. Mutación
updateOrder(id: 12345)que acepta cualquier id porque el resolver confía en el campo de entrada. - Mass assignment en mutations. Mutations que aceptan un input type completo y lo persisten sin filtrar campos sensibles. Un
updateProfile(input: {name, email, role})que olvida bloquearrolepermite escalada a administrador. - Auth bypass por no propagación. El chequeo de autenticación se aplica a la query raíz pero no se propaga a subqueries o fragmentos. Pedir un campo protegido como hijo de otro campo público devuelve el dato sin validar token.
- Field suggestions leak. Mismo problema que en discovery, pero también filtra nombres de campos y tipos privados durante el uso normal de la API, ayudando a mapear funcionalidad oculta.
- SSRF via mutation. Mutations que aceptan URLs como argumento (
importFromUrl,webhookTest,fetchAvatar) y las consultan desde el backend sin validación de destino son una puerta clásica a SSRF contra metadata services de cloud o redes internas. - NoSQL injection en resolvers. Resolvers que pasan argumentos directamente a MongoDB, Elasticsearch u otros stores documentales sin sanitizar permiten operadores tipo
$ne,$regexo$wherey derivan en bypass de autenticación o extracción de datos. - Subscription auth issues. Las suscripciones GraphQL viajan sobre WebSockets con un mensaje inicial
connectionInitdonde se envía el token. Implementaciones que solo validan el token al abrir la conexión y no en cada mensaje permiten secuestrar la suscripción tras expiración o reutilizar conexiones tras logout.
Tooling para pentest GraphQL
La cadena de herramientas habitual combina interceptación, reconstrucción de schema, fuzzing y análisis automatizado.
- Burp Suite + InQL. La extensión InQL para Burp parsea introspection, genera plantillas de queries para todos los tipos y permite enviar mutaciones desde Repeater con autocompletado. Es la base del pentest manual.
- Clairvoyance. Cuando introspection está desactivada, Clairvoyance usa field suggestions para reconstruir el schema iterativamente sobre un diccionario de nombres. Tarda pero recupera la mayor parte de tipos y campos.
- GraphQL Cop. Suite de chequeos automáticos para configuraciones inseguras (introspection abierta, alias overload, batching habilitado, field suggestions, etc.) que sirve como primer pase rápido.
- AutoGraphQL. Herramienta que dado un endpoint genera automáticamente queries y mutaciones de prueba, útil para fuzzing inicial sobre APIs grandes.
- Apollo Studio. Cuando una organización publica su grafo en Apollo Studio sin restricciones, parte de la documentación interna queda accesible públicamente y se convierte en intel gratuito para el red team.
- Hasura Console abuse. Hasura expone una consola administrativa cuya autenticación por defecto es un admin secret. Consolas accesibles sin secret o con secrets débiles entregan control total del backend.
Caso ilustrativo: DoS por nested query
Para ilustrar el patrón sin entregar payload reutilizable, una query construida sobre dos tipos que se referencian mutuamente puede pedir cincuenta niveles de profundidad alternando user { posts { author { posts { author { ... } } } } }. El servidor expande el resolver en cada nivel, dispara una consulta a la base de datos por nodo y consume CPU y memoria exponencialmente. Sin limitador de profundidad o complejidad, una sola petición legítima desde el punto de vista de protocolo agota el worker y, en stacks pequeños, tumba el servicio. El equipo defensivo descubre el ataque porque el dashboard de latencia se dispara, pero el WAF no lo ve porque la petición es sintácticamente válida.
Implementaciones populares y sus pitfalls
Cada implementación tiene su catálogo de configuraciones por defecto que conviene revisar.
- Apollo Server. Introspection activa por defecto en modo desarrollo. La detección automática
process.env.NODE_ENVfalla cuando la variable no se setea correctamente y termina activa en producción. Apollo Sandbox embebido en/graphqlpor defecto. - GraphQL.js (referencia). No incluye limitador de profundidad ni complejidad nativos. Hay que instalar
graphql-depth-limitygraphql-query-complexityexplícitamente. - Hasura. Genera mutaciones CRUD completas para cada tabla de la base de datos. Sin configurar permisos granulares por rol y por columna, cualquier consumidor con acceso al endpoint puede leer y modificar tablas enteras.
- Postgraphile. Mismo patrón que Hasura, genera schema desde Postgres. La autorización delega en políticas RLS de Postgres, que si no están configuradas son permisivas por defecto.
- AWS AppSync. La autorización por defecto puede ser API key, IAM, Cognito u OIDC. Mezclar tipos de autorización en la misma API sin revisar las directivas
@aws_authdeja resolvers accesibles sin token. - Hot Chocolate (.NET). Soporte amplio de federation y subscriptions. Las suscripciones sobre WebSockets requieren configurar explícitamente la validación del
connectionInitpara que el token se valide por mensaje.
Defensas técnicas
Las siguientes contramedidas cubren la mayoría de las familias de ataque descritas y son aplicables independientemente del stack.
- Disable introspection en producción. Salvo necesidad explícita de exponer el schema (federation entre dominios, partners autenticados), la introspección debe estar desactivada en todos los entornos accesibles públicamente. Mantener un dump del schema versionado en repo para el equipo interno.
- Query depth limiter. Limitar la profundidad máxima de anidamiento a un valor razonable según el grafo (típicamente entre cinco y diez niveles). Librerías como
graphql-depth-limitaplican el control en el middleware de validación. - Query complexity analysis. Asignar un coste a cada campo y rechazar queries que superen un presupuesto total.
graphql-query-complexitypermite definir costes estáticos o dinámicos (proporcional al argumentofirsten paginación, por ejemplo). - Persisted queries (APQ). En lugar de aceptar cualquier query del cliente, el servidor mantiene un allowlist de queries pre registradas por hash. El cliente envía solo el hash. Bloquea por completo el abuso de aliasing, depth y batching ad hoc, a costa de un proceso de registro durante el build.
- Rate limiting per field. Adicional al rate limit por IP o token, aplicar límites por campo o resolver costoso (búsqueda full text, exportación, generación de PDF) evita que un cliente legítimo en cuota tumbe un endpoint pesado.
- Auth at resolver level. No confiar en el chequeo del gateway. Cada resolver de tipo debe validar que el principal autenticado tiene acceso al objeto concreto que está resolviendo. Patrones como
dataloadercon verificación de owner por batch son habituales. - Field level authorization. Para campos sensibles dentro de un tipo accesible (ej.
User.emailvisible solo para el propio usuario o admin), aplicar directivas de autorización por campo en lugar de filtrar en cliente. - Disable field suggestions in production. La mayoría de servidores tienen una opción para suprimir sugerencias en errores. Activarla en producción reduce ruido y filtración.
- Subscription auth + connectionInit. Validar el token en
connectionInity revalidar contra revocación o expiración periódicamente durante la vida de la conexión. No asumir que una WebSocket abierta sigue autorizada.
GraphQL y Apollo Federation
Apollo Federation permite componer varios subgrafos (servicios) en un supergrafo expuesto por un gateway. Introduce ventajas operativas claras pero añade superficie de ataque propia.
- Gateway federado. El gateway compone queries y reenvía subqueries a cada subgrafo. Si la autorización se aplica solo en el gateway y los subgrafos confían en su origen, basta con saltarse el gateway y atacar el subgrafo directamente para evadir controles.
- Supergraph security. El schema federado debe distribuirse de forma controlada. Publicar el supergraph en registries accesibles entrega un mapa completo de la arquitectura interna.
- Subgraph isolation. Cada subgrafo debe ser accesible solo desde el gateway (network policies, mTLS, allowlist). Si los subgrafos están en una red interna pero un atacante consigue pivote desde otro servicio comprometido, el daño se amplifica porque no hay autorización extra.
- Entities y
@requires. Las referencias entre subgrafos (@key,@requires) introducen resolvers implícitos que pueden saltarse la validación si no se modelan con cuidado.
Preguntas frecuentes
¿GraphQL es menos seguro que REST?
No intrínsecamente. GraphQL traslada parte de la responsabilidad del servidor al modelo de tipos y a los resolvers. Bien implementado, el catálogo de bugs es comparable al de REST. Mal implementado, multiplica el impacto porque un solo endpoint expone toda la API.
¿Se puede dejar introspection activa en producción?
Solo si hay un caso de uso claro (federation entre partners autenticados, herramientas internas detrás de VPN) y el schema no expone información sensible sobre lógica de negocio o tipos administrativos. En APIs públicas la recomendación es desactivarla y mantener el schema versionado en repo.
¿Un WAF protege una API GraphQL?
Solo parcialmente. Los WAF tradicionales basados en reglas por path o patrones SQLi son ciegos ante alias overload, depth attacks y batching. Existen WAFs y proxies específicos (Apollo Router, productos de seguridad GraphQL) que parsean el AST de la query y aplican controles a ese nivel.
¿Persistir queries rompe la experiencia de desarrollo?
Cambia el flujo pero hay tooling maduro. Apollo, Relay y otros frameworks soportan APQ con generación automática de hashes durante el build. El equipo frontend desarrolla con queries normales y el pipeline las registra antes de desplegar.
¿Quién debe testear externamente una API GraphQL?
Un equipo externo con experiencia específica en GraphQL, no en pentest web genérico. Las herramientas, la metodología y los patrones de bug son distintos. Un pentester acostumbrado solo a REST tarda más y encuentra menos.
¿OWASP API Security Top 10 aplica a GraphQL?
Sí, en su totalidad. BOLA, broken authentication, mass assignment, security misconfiguration y unrestricted resource consumption tienen manifestaciones específicas en GraphQL pero la categoría conceptual es la misma. El Top 10 es la lista mínima de cobertura.
Recursos relacionados
- Pentesting de APIs REST y GraphQL
- Qué es Burp Suite y para qué sirve en pentesting web
- 5 vulnerabilidades web más comunes en 2025
- Qué es SSRF (Server Side Request Forgery)
- Pentesting de aplicaciones web: guía completa
Auditoría GraphQL con Secra
En Secra realizamos pentest específico de APIs GraphQL combinando enumeración manual del schema, fuzzing dirigido sobre resolvers, validación de autorización campo a campo y pruebas controladas de denegación de servicio. La metodología cubre OWASP API Security Top 10 con adaptaciones específicas para GraphQL (introspection, depth, aliasing, batching, federation) y entrega recomendaciones de implementación accionables para el equipo de plataforma.
Si tu organización expone una API GraphQL pública o de partners y necesitas validar que las defensas básicas (introspection, complexity, persisted queries, autorización por resolver) están bien implementadas, puedes contactar con Secra para una propuesta acotada.
Sobre el autor
Equipo de Secra Solutions
Ethical hackers certificados OSCP, OSEP, OSWE, CRTO, CRTL y CARTE, con más de 7 años de experiencia en ciberseguridad ofensiva. Autores de los CVE-2025-40652 y CVE-2023-3512.