Cómo funciona esta comprobación
Scanaris envía una petición POST con la consulta de introspección más pequeña posible, {__schema{queryType{name}}}, a tres rutas donde suele vivir un endpoint GraphQL: /graphql, /api/graphql y /v1/graphql. Si alguna responde 200 y el cuerpo contiene __schema o queryType, la introspección está habilitada y abre un hallazgo de severidad media con la ruta. Si no hay GraphQL en tu web, ninguna ruta responde con eso y no hay hallazgo. La consulta es de solo lectura, no toca datos, y pasa por los mismos límites de tiempo y tamaño que el resto del escaneo.
Por qué importa
La introspección es la función de GraphQL que permite preguntarle a la API por su propio esquema: todos los tipos, todos los campos de cada tipo, todas las consultas y mutaciones disponibles, con sus argumentos. En desarrollo es imprescindible: es lo que usan el playground, el autocompletado del editor y los generadores de código. En producción, abierta a cualquiera, es un mapa completo de tu backend. Un atacante ve que existe una mutación deleteUser(id) o un campo internalNotes sin tener que adivinarlos, y va directo a probar si la autorización de cada uno está bien hecha. Por eso es severidad media: no expone datos por sí misma, pero convierte cualquier otro fallo de permisos en algo trivial de encontrar.
Cómo desactivarla en producción
- Apollo Server:
introspection: "production" !== 'production'al crear el servidor. Desde Apollo 4 ya viene desactivada cuandoNODE_ENVesproduction; si la ves activa, comprueba que esa variable llega a tu despliegue. Desactiva también la landing page o el playground en producción. - GraphQL Yoga y Envelop: el plugin
useDisableIntrospection()de@envelop/disable-introspection, condicionado al entorno. - Hasura (la ruta /v1/graphql): en los ajustes de seguridad del proyecto hay un control de introspección por rol (Cloud y Enterprise). Asegúrate además de que el rol público tiene permisos mínimos y de que el secreto de administrador nunca viaja al navegador.
- graphql-ruby:
disable_introspection_entry_pointsen la clase del esquema. - Python (Graphene, Strawberry, Ariadne): añade la regla
NoSchemaIntrospectionCustomRuledegraphql-corea las reglas de validación en producción. - .NET (Hot Chocolate):
.AllowIntrospection(false)o la opción de modificar la introspección por petición según un header o un rol.
Desactivar la introspección no sustituye a la autorización: cada consulta y mutación debe comprobar quién pregunta. Acompáñala de límites de profundidad y complejidad de consulta (evitan consultas anidadas que tumban el servidor) y, si tu servidor lo ofrece, desactiva también las sugerencias de campos ("Did you mean…"), que filtran nombres aunque la introspección esté cerrada.
Comprueba que está arreglado
curl -s -X POST https://tudominio.com/graphql -H "Content-Type: application/json" -d '{"query":"{__schema{queryType{name}}}"}'
Repite con /api/graphql y /v1/graphql. Esperado: un error del tipo "introspection is not allowed", un 400 o 403, o un 404 si no hay GraphQL ahí. Si ves "queryType":{"name":"Query"}, sigue abierta.
Preguntas frecuentes
Mi API es pública y está documentada. ¿Me afecta?
Si publicas el esquema a propósito, la introspección abierta no revela nada nuevo y puedes dar el hallazgo por aceptado. La mayoría de las APIs de aplicaciones no están en ese caso: tienen mutaciones internas que nadie debería conocer.
Mi endpoint está en otra ruta. ¿No se comprueba?
Solo se prueban las tres rutas más comunes. Si el tuyo vive en otra, haz la prueba de arriba con tu ruta real. Otros paneles y endpoints de desarrollo que no deberían responder en producción los busca endpoints expuestos.
¿Y si el endpoint pide autenticación?
Entonces la introspección responde 401 y no se marca. Es la situación correcta: un atacante sin sesión no obtiene el esquema. Comprueba que la cabecera CORS de la API no esté abierta a cualquier origen; eso lo mira CORS permisivo.