Graphql Introspection in Express
How Graphql Introspection Manifests in Express
Graphql Introspection is a powerful feature that allows clients to query the schema of a GraphQL API, but when exposed in production, it becomes a significant security risk. In Express applications, this manifests when developers use the graphql-express middleware without properly configuring introspection settings.
The most common scenario occurs when developers initialize their GraphQL server like this:
const express = require('express');
const { graphqlHTTP } = require('express-graphql');
const schema = require('./schema');
const app = express();
app.use('/graphql', graphqlHTTP({
schema: schema,
graphiql: true // Often left enabled in development
}));By default, graphql-express enables introspection when graphiql is true or when no explicit introspection option is provided. This creates an attack vector where malicious actors can enumerate all types, queries, mutations, and subscriptions available in your API.
In Express applications, this vulnerability is particularly dangerous because:
- The GraphQL endpoint is typically mounted at a predictable path like
/graphql - Express's middleware stack doesn't provide any built-in protection
- The introspection query can be sent via standard HTTP POST requests
- Attackers can use the discovered schema to craft targeted exploits
A typical introspection attack payload looks like:
{
"query": "{\n __schema {\n types {\n name\n fields {\n name\n type {\n name\n ofType {\n name\n }\n }\n }\n }\n }\n }"
}When sent to an unprotected Express GraphQL endpoint, this returns the complete schema, exposing all available operations, input types, and potential data structures. Attackers can then use this information to identify vulnerable mutations, sensitive queries, or misconfigured resolvers.
Express-specific implementations often compound this risk when developers use additional middleware like authentication or rate limiting after the GraphQL middleware, making it harder to globally disable introspection across all routes.
Express-Specific Detection
Detecting GraphQL introspection vulnerabilities in Express applications requires both manual testing and automated scanning. The most straightforward detection method is to send an introspection query to your GraphQL endpoint and observe the response.
Manual detection can be performed using curl:
curl -X POST http://your-express-app/graphql \
-H "Content-Type: application/json" \
-d '{"query": "{__schema {types {name}}"}'If the server responds with schema information rather than an error or empty response, introspection is enabled.
For automated detection in Express applications, middleBrick provides comprehensive scanning that specifically tests for GraphQL introspection vulnerabilities. The scanner sends the standard introspection query to any GraphQL endpoint it discovers and analyzes the response for schema exposure.
middleBrick's detection approach includes:
- Identifying GraphQL endpoints by analyzing request patterns and content types
- Submitting the introspection query to test for enabled introspection
- Analyzing the response structure to confirm schema exposure
- Checking for GraphiQL or GraphQL Playground interfaces that might indicate development configurations
- Scanning for common GraphQL endpoint paths like
/graphql,/api/graphql, and custom paths
The scanner also examines OpenAPI specifications if provided, looking for GraphQL endpoints and their configuration. This is particularly useful for Express applications that use tools like swagger-ui-express alongside GraphQL.
Another detection method specific to Express is examining the middleware stack. If you're using express-graphql, check your initialization code for missing introspection: false options. The scanner can automatically detect when the GraphQL middleware is configured without proper security settings.
middleBrick's API security scanning takes approximately 5-15 seconds and provides a detailed report showing whether introspection is enabled, the specific GraphQL endpoints discovered, and the completeness of the exposed schema. The report includes severity ratings and specific remediation steps tailored to Express applications.
Express-Specific Remediation
Remediating GraphQL introspection vulnerabilities in Express requires proper configuration of the express-graphql middleware. The most direct fix is to explicitly disable introspection in production environments.
Here's the corrected Express configuration:
const express = require('express');
const { graphqlHTTP } = require('express-graphql');
const schema = require('./schema');
const app = express();
const isProduction = process.env.NODE_ENV === 'production';
app.use('/graphql', graphqlHTTP({
schema: schema,
graphiql: !isProduction,
introspection: !isProduction
}));This configuration ensures that introspection and GraphiQL are only available in non-production environments. For production, both are disabled, preventing unauthorized schema discovery.
For more granular control, you can implement conditional middleware that checks authentication before enabling GraphQL features:
const graphqlMiddleware = graphqlHTTP({
schema: schema,
introspection: false
});
app.use('/graphql', (req, res, next) => {
if (req.headers['x-internal-access'] === 'true') {
return graphqlMiddleware(req, res, next);
}
next();
});This approach allows internal tools to access the GraphQL API while blocking public introspection attempts.
Another Express-specific remediation involves using custom validation middleware before the GraphQL resolver:
const { graphql } = require('graphql');
app.use('/graphql', async (req, res) => {
const { query, variables } = req.body;
// Block introspection queries
if (query && query.includes('__schema')) {
return res.status(403).json({
errors: [{ message: 'Introspection queries are not allowed' }]
});
}
try {
const result = await graphql(schema, query, null, {}, variables);
res.json(result);
} catch (error) {
res.status(400).json({ errors: [error] });
}
});This manual approach gives you complete control over which queries are allowed, though it requires more maintenance than the built-in express-graphql options.
For Express applications using TypeScript or modern JavaScript, you can also implement compile-time checks:
import { GraphQLSchema } from 'graphql';
function createSecureGraphQLMiddleware(schema: GraphQLSchema) {
return graphqlHTTP({
schema,
graphiql: false,
introspection: false,
customFormatErrorFn: (error) => ({
message: error.message,
locations: error.locations,
path: error.path
})
});
}middleBrick's scanning can verify these remediations by testing the GraphQL endpoint after implementation, ensuring that introspection queries are properly blocked and that the API remains functional for legitimate operations.
Related CWEs: dataExposure
| CWE ID | Name | Severity |
|---|---|---|
| CWE-200 | Exposure of Sensitive Information | HIGH |
| CWE-209 | Error Information Disclosure | MEDIUM |
| CWE-213 | Exposure of Sensitive Information Due to Incompatible Policies | HIGH |
| CWE-215 | Insertion of Sensitive Information Into Debugging Code | MEDIUM |
| CWE-312 | Cleartext Storage of Sensitive Information | HIGH |
| CWE-359 | Exposure of Private Personal Information (PII) | HIGH |
| CWE-522 | Insufficiently Protected Credentials | CRITICAL |
| CWE-532 | Insertion of Sensitive Information into Log File | MEDIUM |
| CWE-538 | Insertion of Sensitive Information into Externally-Accessible File | HIGH |
| CWE-540 | Inclusion of Sensitive Information in Source Code | HIGH |