Graphql Introspection in Express with Cockroachdb
Graphql Introspection in Express with Cockroachdb — how this specific combination creates or exposes the vulnerability
GraphQL introspection in an Express backend that uses CockroachDB can expose schema details and operational information when introspection is left enabled in production. Because CockroachDB is often used in distributed and cloud-native environments, the combination of a GraphQL layer and a strongly-consistent SQL store can inadvertently reveal database names, table structures, or query patterns through introspection responses.
In Express, if a GraphQL server is configured to serve introspection queries (commonly via graphqlHTTP with graphqlExpress or express-graphql), an attacker can send an introspection request and receive full type definitions, including custom scalars, directives, and field arguments. When those types map to CockroachDB-backed resolvers, field names and resolver logic may hint at underlying table and column names, primary key design, or how joins are structured. This metadata can aid in crafting injection or privilege escalation attempts, especially if other vulnerabilities such as BOLA/IDOR or improper input validation coexist.
Introspection is useful during development, but in production it should be disabled or gated, particularly when backed by a CockroachDB cluster where schema visibility can amplify risks. Without complementary controls, an unauthenticated attacker can enumerate the API surface and correlate findings with known CVEs affecting dependencies or misconfigured middleware. The risk is compounded when the GraphQL server also exposes an unauthenticated LLM endpoint, enabling prompt injection attempts that leverage discovered schema and data patterns.
middleBrick detects GraphQL introspection exposure as part of its unauthenticated attack surface testing and flags it as a finding, providing remediation guidance to harden the endpoint. This is especially relevant when combined with Cockroachdb-specific configurations, where naming conventions and distributed transactions may further clarify backend behavior to an attacker.
Cockroachdb-Specific Remediation in Express — concrete code fixes
To mitigate introspection risks in an Express application backed by CockroachDB, explicitly disable introspection in production and enforce strict schema exposure controls. Below are concrete code examples that demonstrate secure configuration and safe resolver patterns.
1. Disable introspection in production with express-graphql:
const { graphqlHTTP } = require('express-graphql');
const { buildSchema } = require('graphql');
// Define your schema with types that map to Cockroachdb tables
const schema = buildSchema(`
type User {
id: ID!
name: String!
email: String!
}
type Query {
user(id: ID!): User
}
`);
// Resolver that queries Cockroachdb using a parameterized query
const root = {
user: async ({ id }) => {
const client = await pool.connect();
try {
const res = await client.query('SELECT id, name, email FROM users WHERE id = $1', [id]);
return res.rows[0] || null;
} finally {
client.release();
}
},
};
app.use('/graphql', graphqlHTTP((req) => ({
schema: schema,
rootValue: root,
graphiql: process.env.NODE_ENV !== 'production',
// Explicitly disable introspection in production
customFormatErrorFn: (err) => ({ message: err.message }),
// Allow introspection only in non-production
customValidationRules: () => [(req) => ({
kind: 'Validation',
validate: (rule) => {
if (rule instanceof GraphQLIntrospectionQuery && req.method === 'GET') {
return [{ message: 'Introspection is disabled in production' }];
}
return [];
},
})],
})));
2. Use environment-based introspection control with graphql (low-level):
const { graphql } = require('graphql');
const schema = require('./schema'); // your Cockroachdb-backed schema
async function runQuery(requestString, variables = {}) {
const allowIntrospection = process.env.NODE_ENV !== 'production';
const result = await graphql({
schema,
source: requestString,
variableValues: variables,
validate: (rules) => rules.filter((rule) => {
if (!allowIntrospection && rule.name === 'IntrospectionRule') {
return false;
}
return true;
}),
});
return result;
}
3. Secure CockroachDB connection patterns in Express:
const { Pool } = require('pg');
const pool = new Pool({
connectionString: process.env.COCKROACHDB_URL,
ssl: {
rejectUnauthorized: false, // adjust based on your CA setup
},
});
// Example health check route that does not expose schema
app.get('/healthz', async (req, res) => {
const client = await pool.connect();
try {
const result = await client.query('SELECT 1');
res.json({ status: 'ok', db: result.rows[0] });
} catch (err) {
res.status(500).json({ error: 'database connection issue' });
} finally {
client.release();
}
});
4. Apply schema-based access controls and avoid exposing raw CockroachDB errors:
app.use('/graphql', graphqlHTTP((req) => ({
schema: schema,
rootValue: root,
graphiql: false,
customFormatErrorFn: (err) => {
// Do not expose Cockroachdb-specific error details
return { message: 'Internal server error', code: 'INTERNAL' };
},
})));
These steps reduce the attack surface by limiting introspection, sanitizing errors, and ensuring CockroachDB interactions use parameterized queries to prevent injection-related side-channels that introspection might expose.
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 |