MEDIUM graphql introspectionfeathersjs

Graphql Introspection in Feathersjs

How Graphql Introspection Manifests in Feathersjs

Graphql Introspection is a feature that allows clients to query the schema of a GraphQL API to discover available types, queries, mutations, and fields. While this is useful for development and tooling, it can expose sensitive information in production. In Feathersjs, introspection manifests through the default GraphQL configuration and the way Feathersjs exposes service schemas.

Feathersjs applications typically set up GraphQL using the @feathersjs/graphql module. By default, introspection is enabled, allowing any client to retrieve the complete schema. This becomes problematic when your Feathersjs API exposes internal service structures, database models, or business logic that shouldn't be public knowledge.

The vulnerability appears in several ways in Feathersjs applications:

  • Default schema exposure: Feathersjs automatically generates GraphQL schemas from your services, including field names, types, and relationships that might reveal implementation details
  • Service metadata leakage: Internal service configurations, database table names, and field constraints become visible through introspection queries
  • Business logic exposure: Custom resolvers and computed fields that contain sensitive business logic can be discovered
  • Authentication bypass patterns: Attackers can map out which queries and mutations exist before attempting to bypass authentication

A typical Feathersjs introspection attack might look like this:

query IntrospectionQuery {
  __schema {
    types {
      name
      kind
      description
    }
    queryType {
      name
      fields {
        name
        description
        type {
          name
        }
      }
    }
  }
}

When executed against a Feathersjs GraphQL endpoint, this query returns comprehensive information about all services, their methods, and data structures. For example, if you have a users service with authentication logic, an attacker can discover that a login mutation exists and craft targeted attacks against it.

Feathersjs's schema-first approach means that even custom type definitions in your typeDefs files are exposed. If you've defined internal-only types or enums that represent system states, these become visible to anyone who can reach your GraphQL endpoint.

Feathersjs-Specific Detection

Detecting GraphQL introspection vulnerabilities in Feathersjs requires both manual testing and automated scanning. Here are Feathersjs-specific approaches to identify this issue:

Manual Detection Methods

First, examine your Feathersjs GraphQL setup. In your service initialization, check if you're using the default configuration:

const { graphql } = require('@feathersjs/graphql');
const { schema } = require('./schema');

app.use('/graphql', graphql({ schema, }));

The absence of an introspection option means it's enabled by default. You can test this by sending a simple introspection query to your Feathersjs endpoint:

curl -X POST http://localhost:3030/graphql \
  -H "Content-Type: application/json" \
  -d '{"query": "{\n  __schema {\n    types { name }\n  }\n}"}'

If you receive a response with type information, introspection is enabled.

Using middleBrick for Automated Detection

middleBrick's GraphQL security scanner specifically detects introspection vulnerabilities in Feathersjs applications. The scanner sends standard introspection queries and analyzes the response for exposed schema information. Here's how to scan a Feathersjs API:

npx middlebrick scan http://your-feathersjs-app.com/graphql

The scan tests for:

  • Whether introspection queries succeed without authentication
  • The completeness of schema information returned
  • Exposure of internal service names and field structures
  • Presence of sensitive type definitions

middleBrick provides a security score and specific findings about GraphQL introspection, including whether your Feathersjs endpoint exposes more information than necessary for production use.

Code Review Indicators

During code review of Feathersjs applications, look for these patterns that indicate introspection exposure:

// Vulnerable: Default configuration
app.use('/graphql', graphql({ schema }));

// Better: Explicitly disable in production
const graphqlConfig = {
  schema,
  introspection: process.env.NODE_ENV === 'development'
};
app.use('/graphql', graphql(graphqlConfig));

Also check for the use of makeExecutableSchema from graphql-tools without proper security configurations, and verify that your Feathersjs services don't expose internal-only types through the GraphQL interface.

Feathersjs-Specific Remediation

Remediating GraphQL introspection vulnerabilities in Feathersjs involves both configuration changes and architectural considerations. Here are Feathersjs-specific solutions:

Disable Introspection in Production

The most straightforward fix is to disable introspection when your Feathersjs app runs in production:

const { graphql } = require('@feathersjs/graphql');
const { schema } = require('./schema');

const graphqlConfig = {
  schema,
  introspection: process.env.NODE_ENV === 'development'
};

module.exports = app => {
  app.use('/graphql', graphql(graphqlConfig));
};

This approach allows introspection during development while protecting your production API.

Schema Filtering for Feathersjs

For more granular control, implement schema filtering in your Feathersjs GraphQL setup. This allows you to selectively expose parts of your schema:

const { graphql } = require('@feathersjs/graphql');
const { makeExecutableSchema } = require('@graphql-tools/schema');
const { filterSchema } = require('@graphql-tools/schema');

const originalSchema = makeExecutableSchema({
  typeDefs: fs.readFileSync('./schema.graphql', 'utf8'),
  resolvers: yourResolvers
});

const filteredSchema = filterSchema({
  schema: originalSchema,
  filterIntrospection: process.env.NODE_ENV === 'production',
  ignore: ['Query', 'Mutation'] // Keep root types
});

const graphqlConfig = {
  schema: filteredSchema,
  introspection: process.env.NODE_ENV === 'development'
};

module.exports = app => {
  app.use('/graphql', graphql(graphqlConfig));
};

Feathersjs Service-Level Protection

Combine introspection protection with Feathersjs's built-in authentication and authorization. Modify your service setup to include GraphQL security:

const { authenticate } = require('@feathersjs/authentication');
const { graphql } = require('@feathersjs/graphql');
const { schema } = require('./schema');

module.exports = app => {
  const graphqlConfig = {
    schema,
    introspection: process.env.NODE_ENV === 'development'
  };

  const graphqlService = graphql(graphqlConfig);
  
  // Apply authentication to GraphQL endpoint
  app.use('/graphql', authenticate('jwt'), graphqlService);
};

Custom Introspection Handler

For advanced scenarios, implement a custom introspection handler that returns limited information:

const { graphql } = require('@feathersjs/graphql');
const { schema } = require('./schema');

async function introspectionHandler(request, response) {
  if (process.env.NODE_ENV === 'production') {
    return response.status(403).json({
      error: 'Introspection disabled in production'
    });
  }
  
  // Allow introspection in development
  return graphql({ schema })(request, response);
}

module.exports = app => {
  app.use('/graphql', (req, res, next) => {
    if (req.method === 'POST' && req.body && req.body.query) {
      const firstLine = req.body.query.split('\n')[0];
      if (firstLine.includes('__schema') || firstLine.includes('__type')) {
        return introspectionHandler(req, res);
      }
    }
    return graphql({ schema })(req, res);
  });
};

Monitoring and Logging

Add monitoring to detect introspection abuse attempts:

const { graphql } = require('@feathersjs/graphql');
const { schema } = require('./schema');
const logger = require('./logger');

module.exports = app => {
  app.use('/graphql', (req, res, next) => {
    if (req.body && req.body.query) {
      if (req.body.query.includes('__schema') || req.body.query.includes('__type')) {
        logger.warn('Introspection query detected', {
          ip: req.ip,
          userAgent: req.get('User-Agent'),
          timestamp: new Date().toISOString()
        });
      }
    }
    return graphql({ schema })(req, res);
  });
};

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

Why does Feathersjs enable GraphQL introspection by default?
Feathersjs enables introspection by default to support development workflows and tooling integration. The framework prioritizes developer experience, making it easy to explore schemas during development. However, this convenience becomes a security risk in production environments where sensitive schema information should not be publicly accessible.
Can I completely remove introspection without breaking Feathersjs functionality?
Yes, you can safely disable introspection in production. Feathersjs applications will continue to function normally for clients that have the schema. Most production GraphQL clients, including Feathersjs clients, cache the schema locally and don't need runtime introspection. Tools like GraphQL Playground and Apollo Studio can use schema files instead of introspection queries.