Parte: 4 — Seguridad de aplicaciones web · Fuente: PortSwigger Research / OWASP ⏱️ Duración estimada: 100 min · Nivel: Avanzado
Auditar la seguridad de APIs GraphQL, cada vez más comunes. GraphQL cambia el modelo (un único endpoint, consultas flexibles) e introduce riesgos propios: introspección, over-fetching, ataques de complejidad (DoS), y los mismos problemas de autorización que REST, a menudo peor gestionados.
⚠️ Ética: solo en APIs propias/autorizadas.
Al finalizar, el alumno podrá:
| # | Tema | Por qué importa |
|---|---|---|
| 1 | Modelo GraphQL | Cambia la superficie |
| 2 | Introspección del esquema | Mapa completo de la API |
| 3 | Queries vs. mutations | Lectura y escritura |
| 4 | Autorización en resolvers | Donde suele fallar |
| 5 | Batching y alias | Bypass de rate limit |
| 6 | Ataques de complejidad (DoS) | Disponibilidad |
| 7 | Defensa: límites y authz | Cierre del fallo |
GraphQL cambia el modelo de las APIs REST: en lugar de muchos endpoints fijos, hay un solo
endpoint (/graphql) al que el cliente envía consultas que especifican exactamente qué datos
quiere, con qué campos y qué relaciones. Esa flexibilidad —pedir en una consulta lo que en REST serían
varias llamadas— es su virtud y también su superficie de ataque particular. Muchas defensas
mentales de REST no aplican: no hay "endpoints ocultos" que enumerar (el esquema los expone todos),
pero aparecen problemas nuevos —la autorización dispersa en cada resolver, los ataques de complejidad,
la introspección que regala el mapa completo—. Distinguir queries (leen datos) de mutations
(los modifican) es el primer paso: las mutations son las que cambian estado y las que más importa
proteger.
La característica más relevante para el pentesting es la introspección: GraphQL permite consultar su propio esquema —todos los tipos, campos, argumentos y mutations disponibles— con una consulta especial. Es una funcionalidad de desarrollo pensada para herramientas como GraphiQL, pero si está habilitada en producción, entrega al atacante el mapa completo de la API sin esfuerzo: qué datos existen, qué operaciones se pueden hacer, qué campos hay incluso en tipos que la interfaz nunca usa. Herramientas como GraphQL Voyager o InQL reconstruyen el esquema entero a partir de la introspección. La primera comprobación de un pentest GraphQL es si la introspección está abierta —y la primera recomendación defensiva, casi siempre, desactivarla en producción—.
La trampa conceptual de GraphQL es asumir que, por haber un solo endpoint, basta con proteger ese
endpoint. Falso: la autorización en GraphQL debe vivir en cada resolver —la función que resuelve
cada campo—. Una consulta puede navegar relaciones (usuario { pedidos { direccion } }), y cada salto
debe comprobar que el usuario tiene permiso sobre ese dato. Es el BOLA de la clase 110 trasladado
a GraphQL, y es más fácil de pasar por alto porque la autorización queda dispersa por decenas de
resolvers en vez de en un puñado de endpoints. Un campo sensible sin comprobación en su resolver es una
fuga, aunque el endpoint esté "autenticado".
Dos ataques nacen de la flexibilidad del lenguaje. Los ataques de complejidad / profundidad: como
el cliente compone la consulta, puede pedir estructuras profundamente anidadas o recursivas
(amigos { amigos { amigos { ... } } }) que obligan al servidor a resolver un número explosivo de
operaciones, provocando un DoS con una sola petición. La defensa es limitar la profundidad, la
complejidad y el número de nodos de las consultas, y aplicar timeouts. El batching y los alias: una
sola petición GraphQL puede contener muchas operaciones usando alias (a: login(...) b: login(...)
...), lo que permite evadir el rate limiting que cuenta peticiones HTTP —mil intentos de login en
una sola petición— y acelerar fuerza bruta o enumeración. La defensa exige limitar operaciones por
petición y aplicar el rate limiting a nivel de operación, no de petición HTTP.
La defensa transversal de GraphQL se resume así: desactivar la introspección (y GraphiQL) en producción, comprobar autorización en cada resolver por objeto y por campo, limitar complejidad, profundidad y batching para frenar el DoS y la evasión de límites, y no exponer más campos de los necesarios. Como en REST, la seguridad de GraphQL es, en su núcleo, autorización granular; solo que la flexibilidad del lenguaje añade la complejidad y el batching como vectores propios que un pentester debe probar siempre.
| Término | Definición concisa |
|---|---|
| GraphQL | API de un solo endpoint con lenguaje de consulta |
| Query | Operación que lee datos |
| Mutation | Operación que modifica datos |
| Resolver | Función que resuelve cada campo de una consulta |
| Introspección | Consultar el propio esquema de la API |
| Esquema | Todos los tipos, campos y operaciones disponibles |
| GraphiQL / InQL | Herramientas que reconstruyen el esquema |
| Autorización por resolver | Comprobar el permiso en cada campo, no solo el endpoint |
| BOLA en GraphQL | Acceder a datos de otro navegando relaciones |
| Ataque de complejidad | Consultas anidadas o recursivas que provocan DoS |
| Límite de profundidad | Restringir cuán anidada puede ser una consulta |
| Batching | Muchas operaciones en una sola petición |
| Alias | Repetir operaciones con nombres distintos para evadir límites |
| Rate limiting por operación | Contar operaciones, no peticiones HTTP |
git clone https://github.com/dolevf/Damn-Vulnerable-GraphQL-Application
docker run -t -p 5013:5013 dolevf/dvga
⚠️ Solo en labs propios.
/graphql, /api/graphql) y prueba la introspección:{ __schema { types { name fields { name } } } }
user(id: 2){ email }).En DVGA, obtén el esquema por introspección, explota una autorización rota (query o mutation) y demuestra un bypass de rate limit con batching/alias. Criterio de aceptación: entregas el esquema, la operación no autorizada con su evidencia y el batching que elude el límite, más la defensa (introspección off, authz por resolver, límites de complejidad).
| Síntoma / mensaje | Causa y cómo arreglar |
|---|---|
| Introspección deshabilitada | Usa clairvoyance para inferir el esquema |
| Query da error de tipo | Sintaxis GraphQL; ajusta campos/argumentos |
| Mutation rechazada | Requiere rol; busca otra vía o documenta fortaleza |
| Batching ignorado | El servidor no soporta batch; usa alias |
| DoS no reproducible | Hay límite de profundidad; documenta la defensa |
❓ ¿GraphQL es más inseguro que REST? No inherentemente, pero su flexibilidad y la introspección amplían la superficie y a menudo la autorización está peor implementada.
❓ ¿Basta con desactivar la introspección? Ayuda, pero un atacante puede inferir el esquema (clairvoyance). La defensa real es authz por resolver y límites de complejidad.
❓ ¿Por qué el batching es peligroso? Permite empaquetar muchas operaciones en una petición, eludiendo rate limits diseñados para contar peticiones, no operaciones.
Clase 110 — Seguridad de APIs REST