Graphql Introspection in Express with Bearer Tokens
Graphql Introspection in Express with Bearer Tokens — how this specific combination creates or exposes the vulnerability
GraphQL introspection is a schema-discovery feature that allows clients to query the type system for available queries, mutations, and types. When an Express server exposes a GraphQL endpoint without restricting introspection and also uses Bearer token authentication, the combination can unintentionally expose metadata that aids attackers even when authentication is required.
In this setup, the server accepts HTTP requests with an Authorization header containing a Bearer token, but introspection queries do not enforce the same authorization checks as regular operations. If the GraphQL handler applies introspection filtering inconsistently—allowing introspection regardless of token validity or scope—an unauthenticated or low-privilege client can still retrieve the full schema. This reveals sensitive data modeling choices, relationships, and potentially hints at internal business logic that should remain private.
For example, an attacker can send a POST request with a missing or invalid Bearer token and still receive a 200 response with schema details if introspection is enabled. The presence of a Bearer token requirement on some routes but not consistently on introspection creates an authorization bypass vector. Attackers can use this to map the API surface, identify hidden fields, and craft further attacks such as BOLA/IDOR or Property Authorization issues.
When combined with other checks run by middleBrick—such as Authentication, BOLA/IDOR, and Property Authorization—this misconfiguration is detectable. middleBrick scans the unauthenticated attack surface and can flag introspection exposure even when Bearer tokens are present but not uniformly enforced, providing prioritized findings with severity and remediation guidance.
Using the OpenAPI/Swagger spec analysis feature, middleBrick cross-references spec definitions with runtime behavior. If the spec documents introspection under security schemes but runtime responses leak schema data without proper token validation, the scan will highlight the inconsistency.
Bearer Tokens-Specific Remediation in Express — concrete code fixes
To secure GraphQL introspection in Express when using Bearer tokens, enforce authorization checks before allowing introspection queries. Below are concrete code examples demonstrating how to implement this correctly.
1. Middleware to validate Bearer tokens
Use an Express middleware that verifies the presence and validity of a Bearer token. This example uses a simple bearer-token check against a known value; in production, integrate with your identity provider or token verification library.
const jwt = require('jsonwebtoken');
function verifyBearerToken(req, res, next) {
const authHeader = req.headers.authorization;
if (!authHeader || !authHeader.startsWith('Bearer ')) {
return res.status(401).json({ error: 'Unauthorized: missing Bearer token' });
}
const token = authHeader.split(' ')[1];
try {
const decoded = jwt.verify(token, process.env.JWT_SECRET);
req.user = decoded;
return next();
} catch (err) {
return res.status(401).json({ error: 'Unauthorized: invalid token' });
}
}
2. Apply token verification to GraphQL endpoint
Apply the token verification middleware to the GraphQL route. This ensures that introspection and all other operations require a valid Bearer token.
const express = require('express');
const { graphqlHTTP } = require('express-graphql');
const schema = require('./schema');
const app = express();
app.use('/api/graphql', verifyBearerToken, graphqlHTTP((req, res, graphQLParams) => ({
schema,
graphiql: false,
// Explicitly disable introspection for unauthorized requests
customFormatErrorFn: (error) => ({
message: error.message,
code: error.extensions?.code || 'INTERNAL_SERVER_ERROR',
}),
})));
app.listen(3000, () => console.log('Server running on port 3000'));
3. Disable introspection conditionally
You can further tighten security by disabling introspection entirely for production or allowing it only for specific trusted roles. The example below disables introspection unless a special scope is present in the token.
function introspectionAllowed(req) {
return req.user && req.user.scopes && req.user.scopes.includes('schema:read');
}
app.use('/api/graphql', verifyBearerToken, graphqlHTTP((req, res, graphQLParams) => ({
schema,
graphiql: false,
customFormatErrorFn: (error) => ({
message: error.message,
code: error.extensions?.code || 'INTERNAL_SERVER_ERROR',
}),
// Disable introspection unless authorized
customResolverMiddleware: (next) => (root, args, context, info) => {
const isIntrospection = info.fieldName === '__schema' || info.fieldName === '__type';
if (isIntrospection && !introspectionAllowed(context.req)) {
throw new Error('Introspection is not allowed');
}
return next(root, args, context, info);
},
})));
4. Validate and log suspicious requests
Log introspection attempts that lack proper authorization to detect reconnaissance activity. Combine this with rate limiting to mitigate abuse.
app.use('/api/graphql', verifyBearerToken, (req, res, next) => {
const graphQLParams = req.body;
if (graphQLParams && graphQLParams.query && graphQLParams.query.includes('__schema')) {
if (!introspectionAllowed(req)) {
console.warn(`Introspection attempt without proper scope from ${req.ip}`);
}
}
next();
}, graphqlHTTP(/* same as above */));
By applying these patterns, Express services that use Bearer tokens can prevent unauthorized schema exposure while maintaining usability for authenticated clients. These measures align with checks performed by middleBrick, such as Authentication, BOLA/IDOR, and Property Authorization, and help ensure that introspection is appropriately gated.
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 |