Graphql Introspection Abuse in Adonisjs
How Graphql Introspection Abuse Manifests in Adonisjs
GraphQL introspection allows clients to query the schema for types, fields, and directives. While useful for development tools, exposing introspection in production can leak sensitive API structure, enabling attackers to map hidden endpoints, administrative mutations, or deprecated fields. In AdonisJS applications using @adonisjs/graphql-server, introspection is enabled by default in development but may remain active in production if not explicitly disabled.
A common misconfiguration occurs when developers copy development settings to production without adjusting the GraphQL server options. For example, in start/graphql.ts, the GraphQLServer instance might be initialized without setting introspection: false:
import { GraphQLServer } from '@adonisjs/graphql-server'
const graphqlServer = new GraphQLServer(
schema,
{
// Missing introspection: false in production
playground: false,
// No introspection flag → defaults to true
}
)
graphqlServer.start()
This exposes the full schema via introspection queries like:
{
__schema {
types {
name
fields { name }
}
}
}
Attackers can use this to discover:
- Hidden mutations like
deleteUserorupdateRole - Deprecated fields that may lack proper authorization
- Relationships that expose indirect access to sensitive data (e.g.,
User.creditCard) - Custom scalar types that might indicate unsafe handling (e.g.,
JSONscalars)
In AdonisJS, this risk is amplified when combined with misconfigured guards. For instance, if a mutation like updateUserRole relies on a guard that fails to check for admin privileges due to a typo (role === 'adim'), introspection reveals the mutation’s existence, allowing privilege escalation. CVE-2020-28477 demonstrated similar schema exposure leading to unauthorized data access in GraphQL endpoints.
Adonisjs-Specific Detection
Detecting GraphQL introspection exposure in AdonisJS requires checking both runtime behavior and configuration. middleBrick identifies this by sending an introspection query to the /graphql endpoint and analyzing the response for schema data. It does not require authentication, scanning the unauthenticated surface as part of its black-box assessment.
During a scan, middleBrick checks for:
- Presence of
__schemaor__typefields in the response - Return of type definitions, field names, or directive information
- Response times indicating reflective processing (not cached errors)
To manually test, use curl to send an introspection query:
curl -X POST https://api.example.com/graphql \
-H "Content-Type: application/json" \
-d '{"query":"{ __schema { types { name } } }"}'
A successful response returns JSON with a data.__schema.types array. If the server returns an error like Introspection is disabled or Cannot query field '__schema', introspection is properly restricted.
middleBrick also correlates findings with AdonisJS-specific patterns. For example, if the endpoint is served via AdonisJS and the response includes headers like x-powered-by: AdonisJs, the scanner notes the framework context. It cross-references this with the GraphQL server configuration: if introspection is enabled in a non-development environment (detected via NODE_ENV hints in headers or error messages), it flags the issue under the "Data Exposure" category with medium severity.
Additionally, middleBrick checks for related risks: if introspection exposes a mutation like execSql (a known dangerous pattern in some legacy AdonisJS plugins), it elevates the severity due to potential SQL injection chains.
Adonisjs-Specific Remediation
Disabling introspection in AdonisJS is straightforward and should be done in production environments. The @adonisjs/graphql-server package accepts an introspection boolean in its constructor. Set this to false when NODE_ENV is not development.
Update start/graphql.ts to conditionally disable introspection:
import { GraphQLServer } from '@adonisjs/graphql-server'
import Env from '@ioc:Adonis/Core/Env'
const graphqlServer = new GraphQLServer(
schema,
{
playground: Env.get('NODE_ENV') === 'development',
introspection: Env.get('NODE_ENV') === 'development', // Disable in prod/staging
}
)
graphqlServer.start()
This ensures introspection is only available when explicitly needed for development. For environments where you want to retain introspection for internal tools (e.g., staging), consider:
- Using environment-specific schemas (e.g.,
schema.staging.tswith introspection enabled) - Deploying a separate GraphQL endpoint for tooling, protected by network policies or VPN
- Using Apollo Studio or GraphQL Voyager behind authentication, not exposing the raw endpoint
If you must keep introspection enabled for legitimate reasons (e.g., public API documentation), implement additional controls:
- Rate limiting: Use AdonisJS’s built-in
RateLimitermiddleware to restrict introspection queries. Apply it selectively to the GraphQL route instart/routes.ts:
Route.group(() => {
Route.post('/', 'GraphqlController.handle')
}).middleware(['auth', 'rateLimiter:10']) // 10 requests/minute
- Query depth limiting: Prevent expensive introspection queries by limiting query depth. While
@adonisjs/graphql-serverdoesn’t include this by default, you can wrap the schema withgraphql-depth-limit:
import { depthLimit } from 'graphql-depth-limit'
const schemaWithLimits = applyMiddleware(schema, depthLimit(5))
const graphqlServer = new GraphQLServer(schemaWithLimits, { introspection: true })
After remediation, verify the fix by attempting an introspection query. It should return an error such as:
{
"errors": [
{
"message": "Introspection is disabled",
"locations": [{ "line": 1, "column": 2 }],
"path": ["__schema"]
}
]
}
Regularly scan with middleBrick to ensure introspection remains disabled across deployments, especially after dependency updates or configuration changes.
Frequently Asked Questions
Does disabling GraphQL introspection in AdonisJS break GraphQL IDEs like Apollo Studio or GraphiQL?
.graphql file or exporting it from your AdonisJS server during development. In production, IDEs should not require access to the live endpoint; instead, use schema publishing tools (like Apollo GraphOS) to share the schema securely with stakeholders.Can middleBrick detect if introspection is disabled but the GraphQL endpoint still leaks schema information through error messages?
__schema or __type in a successful response.