HIGH graphql introspectiondocker

Graphql Introspection on Docker

How Graphql Introspection Manifests in Docker

GraphQL introspection is a powerful feature that allows clients to query the schema of a GraphQL API, but when exposed in Dockerized environments, it creates significant security risks. In Docker deployments, GraphQL servers often run as containers with predictable configurations, making them easier targets for attackers who can discover exposed introspection endpoints.

The most common manifestation occurs when GraphQL servers are deployed without proper security middleware. For example, a Node.js GraphQL server running in a Docker container might expose the standard /graphql endpoint with introspection enabled by default. Attackers can send IntrospectionQuery requests to enumerate all types, fields, mutations, and subscriptions available in the API.

In Docker environments, this becomes more dangerous because:

  • Containers often use predictable naming conventions and network configurations
  • Development environments frequently mirror production setups
  • GraphQL servers might be exposed on multiple ports (8080, 4000, 5000) across different containers
  • Service discovery tools can automatically locate GraphQL endpoints

A typical vulnerable setup in Docker might look like this in a docker-compose.yml:

version: '3.8'
services:
  graphql-api:
    build: .
    ports:
      - "4000:4000"
    environment:
      - NODE_ENV=development
    volumes:
      - ./src:/app/src

This configuration exposes the GraphQL endpoint on port 4000 without any authentication or rate limiting. An attacker could then send a simple introspection query:

{
  __schema {
    types {
      name
      fields {
        name
      }
    }
  }
}

The response reveals the complete API structure, including sensitive field names, argument types, and available mutations. In Docker environments, this information can be combined with container metadata to identify potential attack vectors across the entire microservices architecture.

Another Docker-specific manifestation occurs when GraphQL servers are proxied through Nginx or similar reverse proxies. If the proxy configuration doesn't properly handle GraphQL introspection headers, attackers can bypass security controls by targeting the internal GraphQL container directly through the Docker network.

Docker-Specific Detection

Detecting GraphQL introspection vulnerabilities in Docker environments requires both network-level and application-level scanning. The most effective approach combines automated scanning with manual verification of container configurations.

Network-level detection involves scanning exposed ports across all Docker containers. Tools like docker ps and docker network ls can identify running containers and their network configurations. For GraphQL endpoints specifically, you can use curl to test for introspection support:

# Scan all running containers for GraphQL endpoints
for container_id in $(docker ps -q); do
  container_name=$(docker inspect --format='{{.Name}}' $container_id)
  container_ports=$(docker inspect --format='{{range .NetworkSettings.Ports}}{{.}} {{end}}' $container_id)
  
  # Test common GraphQL ports
  for port in 4000 8080 5000; do
    if echo $container_ports | grep -q ":$port"; then
      echo "Testing $container_name on port $port"
      curl -s -X POST http://localhost:$port/graphql \
        -H "Content-Type: application/json" \
        -d '{"query":"{__schema{types{name}}}"}' | grep -q "__schema" && \
        echo "  ✓ Introspection enabled"
    fi
  done
done

This script identifies containers with exposed GraphQL endpoints and tests whether introspection is enabled. However, manual testing alone is insufficient for production environments.

middleBrick provides automated GraphQL introspection scanning specifically designed for Docker environments. The tool performs black-box scanning of API endpoints without requiring credentials or configuration. When scanning Docker-deployed APIs, middleBrick:

  • Detects exposed GraphQL endpoints across all network interfaces
  • Tests for introspection support using standardized queries
  • Analyzes the returned schema for sensitive information exposure
  • Checks for default configurations that might indicate development environments

The scanning process takes 5-15 seconds and provides a security risk score (A-F) with prioritized findings. For GraphQL introspection specifically, middleBrick checks whether the endpoint allows unauthenticated access to schema information, which is a critical security finding.

Additional detection methods include:

# Check Docker logs for GraphQL errors that might reveal schema information
docker logs $(docker ps -q) 2>&1 | grep -i "graphql\|schema" | head -10

# Scan for common GraphQL error messages that expose internal structure
docker exec $(docker ps -q) \
  curl -s -X POST http://localhost:4000/graphql \
  -H "Content-Type: application/json" \
  -d '{"query":"{__type(name: \"User\"){name}}", "variables":null}' | \
  grep -E "(Cannot query field|Unknown type)"

These commands help identify containers that might be leaking schema information through error messages or debug output.

Docker-Specific Remediation

Remediating GraphQL introspection vulnerabilities in Docker environments requires a multi-layered approach that combines configuration changes, code modifications, and deployment best practices. The goal is to disable introspection in production while maintaining it for development and testing environments.

The most straightforward remediation is to disable introspection at the GraphQL server level. For Apollo Server in Node.js, this can be done in the server configuration:

const { ApolloServer, gql } = require('apollo-server');

const typeDefs = gql`
  type Query {
    hello: String
  }
`;

const resolvers = {
  Query: {
    hello: () => 'Hello world!'
  }
};

const server = new ApolloServer({
  typeDefs,
  resolvers,
  introspection: process.env.NODE_ENV === 'development',
  playground: process.env.NODE_ENV === 'development'
});

server.listen({ port: 4000 }).then(({ url }) => {
  console.log(`Server ready at ${url}`);
});

This configuration uses the NODE_ENV environment variable to control whether introspection and the GraphQL playground are enabled. In Docker, this translates to:

FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
EXPOSE 4000
ENV NODE_ENV=production
CMD ["node", "server.js"]

For Docker Compose, you can override the environment variable for development:

version: '3.8'
services:
  graphql-api:
    build: .
    ports:
      - "4000:4000"
    environment:
      - NODE_ENV=production
    deploy:
      resources:
        limits:
          memory: 512M

Another effective approach is implementing middleware that blocks introspection queries. This can be done with custom Apollo Server plugins:

const { ApolloServer, gql } = require('apollo-server');
const url = require('url');

const server = new ApolloServer({
  typeDefs,
  resolvers,
  plugins: [
    {
      requestDidStart: () => ({
        didResolveOperation: async (requestContext) => {
          const { query } = requestContext.request;
          if (query.includes('__schema') || query.includes('__type')) {
            throw new Error('Introspection queries are not allowed in production');
          }
        }
      })
    }
  ]
});

For Docker deployments, you can also implement network-level controls using Nginx as a reverse proxy:

upstream graphql_api {
    server graphql:4000;
}

server {
    listen 80;
    server_name graphql.example.com;

    location /graphql {
        if ($arg_query ~* "__schema|__type") {
            return 403;
        }
        proxy_pass http://graphql_api;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}

This configuration blocks introspection queries at the proxy level before they reach the GraphQL server. In Docker Compose, you would add an Nginx service that proxies to the GraphQL container.

Additional remediation steps include:

# Update Docker Compose to use a network with proper isolation
version: '3.8'
services:
  graphql-api:
    build: .
    networks:
      - api-network
    environment:
      - NODE_ENV=production

  nginx:
    image: nginx:alpine
    ports:
      - "80:80"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf
    networks:
      - api-network
    depends_on:
      - graphql-api

networks:
  api-network:
    driver: bridge

This setup isolates the GraphQL API on a dedicated network and only exposes it through the Nginx reverse proxy, which implements the introspection blocking rules.

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

How can I test if my Dockerized GraphQL API has introspection enabled?
Use a simple curl command to test for introspection support: curl -X POST -H "Content-Type: application/json" -d '{"query":"{__schema{types{name}}}"}' http://localhost:4000/graphql. If you receive a response containing schema information, introspection is enabled. For automated testing in Docker environments, middleBrick can scan your API endpoints in 5-15 seconds and provide a security risk score with specific findings about introspection vulnerabilities.
What's the difference between disabling introspection and blocking introspection queries?
Disabling introspection at the GraphQL server level prevents the server from processing introspection queries at all, which is the most secure approach. Blocking introspection queries at the proxy or middleware level allows the server to receive the queries but returns an error or blocks them before processing. The first approach is more secure but may break legitimate development tools. The second approach provides a security layer while maintaining some functionality, though it's less secure than complete disabling.