HIGH graphql introspectioncassandra

Graphql Introspection in Cassandra

How GraphQL Introspection Manifests in Cassandra

When a GraphQL API uses Apache Cassandra as its data store, introspection queries can reveal more than just the GraphQL schema; they can indirectly expose the underlying Cassandra data model. An attacker begins by sending a standard introspection request such as:

{
  __schema {
    types { name kind fields { name type { name kind } } }
  }
}

The response lists every GraphQL type, its fields, and the associated types. In a Cassandra‑backed implementation, each GraphQL type often maps directly to a Cassandra table (or a user‑defined type) and each field maps to a column. By enumerating the types and fields, an attacker can infer table names, column names, and even the primary‑key structure without needing any credentials.

This information becomes dangerous when the GraphQL resolvers construct CQL statements by concatenating user‑supplied input. For example, a resolver that fetches a user profile might look like this in Java using the DataStax driver:

public ResultSet getUser(String userId) {
  String cql = "SELECT * FROM users WHERE user_id = '" + userId + "';
  return session.execute(cql);
}

If the resolver receives the userId from a GraphQL argument, an attacker who knows the table name (users) and column (user_id) can inject additional CQL via the argument, turning a simple lookup into a data‑exfiltration or even a destructive operation. The introspection step is the reconnaissance phase that makes such injection feasible because it tells the attacker exactly which table and column to target.

Thus, GraphQL introspection in a Cassandra context does two things: (1) it leaks the logical data model, and (2) it enables attackers to craft precise, schema‑aware CQL injection payloads when resolvers are not using prepared statements or proper input validation.

Cassandra-Specific Detection

middleBrick includes GraphQL introspection as part of its Input Validation and Data Exposure checks. When scanning an endpoint, it automatically sends a set of introspection queries (including __schema, __type, and fragmented variations) and analyses the responses for:

  • Presence of type definitions that map to Cassandra tables (e.g., types named after known Cassandra tables or containing fields that resemble column names).
  • Absence of authentication or authorization on the introspection endpoint.
  • Indicators that resolvers may be building CQL strings unsafely (detected via patterns in error messages or unexpected data in the response).

To run this check from the command line, you would use the middleBrick CLI:

middlebrick scan https://api.example.com/graphql

The tool returns a JSON report that includes a finding such as:

{
  "id": "GRAPHQL-INTROSPECTION-001",
  "name": "GraphQL Introspection Enabled",
  "severity": "medium",
  "description": "The API exposes __schema and __type fields, revealing the underlying Cassandra table/column structure.",
  "remediation": "Disable introspection in production or restrict it to authenticated users."
}

In the Dashboard, the finding appears under the "Input Validation" category with a trend line showing whether the issue persists across scans. The GitHub Action can be configured to fail a build if this finding is present, preventing the deployment of an API that unintentionally leaks its Cassandra schema.

Cassandra-Specific Remediation

The primary remediation is to prevent unauthenticated introspection from revealing the Cassandra data model. This can be done at the GraphQL layer and reinforced with Cassandra‑native controls.

1. Disable or restrict introspection

If you are using Apollo Server (Node.js), set the introspection flag to false in production:

const { ApolloServer } = require('@apollo/server');
const { startStandaloneServer } = require('@apollo/server/standalone');

const server = new ApolloServer({
  typeDefs,          // your GraphQL schema
  resolvers,
  introspection: false   // disables __schema/__type
});

startStandaloneServer(server, { listen: { port: 4000 } });

If you need introspection for internal tooling, guard it with authentication or IP‑allow lists using middleware:

server.applyMiddleware({ app, path: '/graphql', cors: false });
app.use('/graphql', (req, res, next) => {
  if (req.body.query && req.body.query.includes('__schema')) {
    // allow only from trusted internal IPs
    if (!req.ip.startsWith('10.')) {
      return res.status(403).send('Introspection disabled');
    }
  }
  next();
});

2. Use prepared statements in resolvers

Avoid string concatenation when building CQL. The DataStax driver supports prepared statements that automatically handle parameterization:

const cassandra = require('cassandra-driver');
const session = new cassandra.Client({ contactPoints: ['127.0.0.1'], localDataCenter: 'datacenter1', keyspace: 'mykeyspace' });

const getUserStmt = session.prepare('SELECT * FROM mykeyspace.users WHERE user_id = ?');

async function getUser(parent, args, context, info) {
  const [result] = await session.execute(getUserStmt, [args.userId]);
  return result ? result.row() : null;
}

Because the userId is bound as a parameter, even if an attacker knows the table name from introspection, they cannot inject additional CQL.

3. Apply Cassandra role‑based access control (RBAC)

Create a dedicated role for the API user and grant only the necessary permissions:

CREATE ROLE api_user WITH PASSWORD = 'StrongPass!2025' LOGIN = true;
GRANT SELECT, INSERT ON KEYSPACE mykeyspace TO api_user;
-- optionally restrict to specific tables
GRANT SELECT ON TABLE mykeyspace.users TO api_user;

Configure the DataStax driver to connect using this role:

const auth = new cassandra.auth.PlainTextAuthProvider('api_user', 'StrongPass!2025');
const session = new cassandra.Client({ contactPoints: ['127.0.0.1'], localDataCenter: 'datacenter1', keyspace: 'mykeyspace', authProvider: auth });

With RBAC in place, even if an attacker discovers a table name via introspection, they cannot read or modify data without valid credentials.

4. Enable audit logging

Turn on Cassandra’s built‑in audit log to capture any unexpected queries:

ALTER SYSTEM SET audit_logging_options = {'enabled': 'true', 'logger': 'SLF4JAuditWriter', 'retention_time': '86400'};

Reviewing the audit log helps detect attempts to query system_schema tables or to execute malformed CQL that may have originated from a GraphQL resolver.

By combining GraphQL‑level introspection restrictions, prepared‑statement resolvers, Cassandra RBAC, and audit logging, you eliminate the information leakage that introspection provides and close the path to CQL injection.

Related CWEs: dataExposure

CWE IDNameSeverity
CWE-200Exposure of Sensitive Information HIGH
CWE-209Error Information Disclosure MEDIUM
CWE-213Exposure of Sensitive Information Due to Incompatible Policies HIGH
CWE-215Insertion of Sensitive Information Into Debugging Code MEDIUM
CWE-312Cleartext Storage of Sensitive Information HIGH
CWE-359Exposure of Private Personal Information (PII) HIGH
CWE-522Insufficiently Protected Credentials CRITICAL
CWE-532Insertion of Sensitive Information into Log File MEDIUM
CWE-538Insertion of Sensitive Information into Externally-Accessible File HIGH
CWE-540Inclusion of Sensitive Information in Source Code HIGH

Frequently Asked Questions

Can GraphQL introspection be safely left enabled in a Cassandra‑backed API when the API is behind a VPN?
Even with network restrictions, introspection still reveals the Cassandra table and column structure to anyone who can reach the endpoint. If an attacker gains VPN access (e.g., via compromised credentials or a misconfigured device), they can use that schema information to craft precise CQL injection payloads. The safest approach is to disable introspection in production or restrict it to authenticated, authorized users only.
How does middleBrick differentiate between harmless introspection and a dangerous exposure of Cassandra schema?
middleBrick checks the introspection response for type and field names that match common Cassandra naming patterns (e.g., types ending in '_tbl' or fields that resemble column names such as 'user_id', 'created_at'). It also correlates the presence of introspection with missing authentication or with resolver patterns that build CQL strings unsafely. When both schema exposure and unsafe resolver indicators are present, the finding is raised as a medium‑or‑high severity issue.