HIGH graphql introspectionfastapi

Graphql Introspection in Fastapi

How Graphql Introspection Manifests in Fastapi

Graphql Introspection is a powerful feature that allows clients to query the schema of a GraphQL API, but when exposed in production Fastapi applications, it creates significant security risks. Fastapi developers often enable introspection by default when setting up GraphQL endpoints using libraries like strawberry or ariadne, unaware of the security implications.

In Fastapi, Graphql Introspection typically manifests through the /graphql endpoint where attackers can send introspection queries like:

query {
  __schema {
    types {
      name
      fields {
        name
        type {
          name
          ofType {
            name
          }
        }
      }
    }
  }
}

This query returns the complete schema including all types, mutations, queries, and even private fields that shouldn't be exposed publicly. Fastapi applications using strawberry-graphql often have this enabled by default:

from fastapi import FastAPI
from strawberry.fastapi import GraphQLRouter

app = FastAPI()

@strawberry.type
class Query:
    @strawberry.field
    def hello(self) -> str:
        return "Hello World"

@strawberry.type
class Mutation:
    @strawberry.mutation
    def create_user(self, email: str) -> bool:
        # Implementation
        return True

schema = strawberry.Schema(query=Query, mutation=Mutation)
app.include_router(GraphQLRouter(schema))

The above Fastapi code exposes the entire GraphQL schema to anyone who can access the endpoint. Attackers can use this information to craft targeted queries, discover hidden endpoints, identify vulnerable patterns, and even find business logic flaws. For instance, they might discover a deleteUser mutation that wasn't intended for public use, or identify that certain fields return sensitive data like user.email or user.passwordHash.

Fastapi's async nature makes GraphQL particularly attractive, but this also means introspection attacks can be executed rapidly and in parallel, potentially overwhelming the API. The introspection data reveals field argument types, which helps attackers craft parameter manipulation attacks. For example, if they see a searchUsers(email: String!) field, they can immediately attempt SQL injection or NoSQL injection patterns.

Another manifestation occurs when Fastapi applications use GraphQL subscriptions. Introspection reveals subscription types and their arguments, allowing attackers to subscribe to real-time data streams they shouldn't access, potentially leading to data exfiltration or denial of service through subscription flooding.

Fastapi-Specific Detection

Detecting Graphql Introspection vulnerabilities in Fastapi applications requires both manual testing and automated scanning. The most straightforward detection method is sending a standard introspection query to your GraphQL endpoint and examining the response.

Here's a Fastapi-specific detection script you can run:

import requests

# Fastapi GraphQL endpoint (common pattern)
url = "http://localhost:8000/graphql"

# Standard introspection query
query = '''
query IntrospectionQuery {
  __schema {
    types {
      name
      kind
      description
      fields {
        name
        description
        args {
          name
          description
          type {
            name
            kind
          }
        }
        type {
          name
          kind
        }
      }
    }
  }
}
'''

response = requests.post(url, json={'query': query})

if response.status_code == 200:
    data = response.json()
    if '__schema' in data.get('data', {}):
        print("✓ Introspection is enabled")
        print(f"Schema contains {len(data['data']['__schema']['types'])} types")
        
        # Check for sensitive patterns
        sensitive_fields = []
        for type_def in data['data']['__schema']['types']:
            for field in type_def.get('fields', []):
                if 'password' in field['name'].lower() or 'secret' in field['name'].lower():
                    sensitive_fields.append(f"{type_def['name']}.{field['name']}")
        
        if sensitive_fields:
            print("⚠ Found potentially sensitive fields:")
            for field in sensitive_fields:
                print(f"  - {field}")
    else:
        print("✗ Introspection query failed or endpoint not GraphQL")
else:
    print(f"✗ HTTP {response.status_code}")

For automated detection in Fastapi applications, middleBrick provides specialized GraphQL scanning that tests for introspection exposure without requiring credentials. The scanner sends introspection queries to your Fastapi GraphQL endpoints and analyzes the response for schema exposure, sensitive field names, and potential attack vectors.

middleBrick's Fastapi-specific detection includes:

  • Introspection query execution and schema analysis
  • Detection of Fastapi-specific GraphQL patterns (strawberry, ariadne, graphene)
  • Identification of sensitive field names and argument types
  • Analysis of subscription capabilities and real-time endpoints
  • Cross-referencing with OpenAPI specs if available

You can scan your Fastapi GraphQL API using the middleBrick CLI:

npm install -g middlebrick
middlebrick scan https://your-fastapi-app.com/graphql --category graphql

The scanner tests for introspection in under 15 seconds and provides a security score with specific findings about schema exposure, sensitive fields, and remediation guidance tailored to Fastapi applications.

Fastapi-Specific Remediation

Remediating Graphql Introspection in Fastapi requires a multi-layered approach. The most straightforward method is disabling introspection in production using Fastapi's GraphQL library configuration.

For strawberry-graphql in Fastapi:

from fastapi import FastAPI
from strawberry.fastapi import GraphQLRouter

app = FastAPI()

@strawberry.type
class Query:
    @strawberry.field
    def hello(self) -> str:
        return "Hello World"

@strawberry.type
class Mutation:
    @strawberry.mutation
    def create_user(self, email: str) -> bool:
        return True

schema = strawberry.Schema(query=Query, mutation=Mutation)

# Disable introspection in production
if app.env == "production":
    graphql_router = GraphQLRouter(
        schema,
        introspection=False  # Disable introspection queries
    )
else:
    graphql_router = GraphQLRouter(schema)

app.include_router(graphql_router, prefix="/graphql")

For ariadne in Fastapi:

from fastapi import FastAPI
from ariadne import QueryType, make_executable_schema, graphql
from ariadne.contrib.fasted import GraphQL

app = FastAPI()

type_defs = """
type Query {
    hello: String!
}
"""

query = QueryType()

@query.field("hello")
async def resolve_hello(*_):
    return "Hello World"

schema = make_executable_schema(type_defs, query)

@app.post("/graphql")
async def graphql_server(state, request: Request):
    # Custom middleware to block introspection queries
    data = await request.json()
    if data.get("query").strip().lower().startswith("query {__schema"):
        return JSONResponse(
            status_code=403,
            content={"errors": [{"message": "Introspection disabled"}]}
        )
    
    success, result = await graphql.subscribe(schema, data) if state.get("is_subscription") else graphql(schema, data)
    return JSONResponse(result)

A more robust approach uses Fastapi middleware to filter introspection queries:

from fastapi import Request, Response
from fastapi.middleware.base import BaseHTTPMiddleware

class GraphQLIntrospectionMiddleware(BaseHTTPMiddleware):
    async def dispatch(self, request: Request, call_next):
        if request.method == "POST" and request.url.path == "/graphql":
            data = await request.json()
            query = data.get("query", "").strip()
            
            # Block introspection queries (case-insensitive)
            if query.lower().startswith("query {__schema") or query.lower().startswith("query {__type"):
                return Response(
                    content={"errors": [{"message": "Introspection disabled in production"}]},
                    media_type="application/json",
                    status_code=403
                )
        
        return await call_next(request)

app.add_middleware(GraphQLIntrospectionMiddleware)

For comprehensive protection, combine these approaches with role-based access control:

from fastapi import Depends, HTTPException
from fastapi.security import OAuth2PasswordBearer
from functools import wraps

oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")

def require_graphql_admin(fn):
    @wraps(fn)
    async def wrapper(*args, **kwargs):
        # Check if user has admin role
        # This is simplified - implement proper auth
        if not hasattr(args[0], 'user') or not args[0].user.get('is_admin'):
            raise HTTPException(status_code=403, detail="Admin access required")
        return await fn(*args, **kwargs)
    return wrapper

# Apply to specific mutations
@strawberry.type
class AdminMutation:
    @strawberry.mutation
    @require_graphql_admin
    def delete_user(self, user_id: int) -> bool:
        # Only accessible to authenticated admins
        return True

Finally, implement logging and monitoring for GraphQL queries to detect suspicious patterns:

from fastapi import BackgroundTasks

async def log_graphql_query(query: str, user_id: int = None):
    # Log to database or external service
    pass

@app.post("/graphql")
async def graphql_endpoint(
    state, 
    request: Request,
    background_tasks: BackgroundTasks
):
    data = await request.json()
    query = data.get("query", "")
    
    # Log all queries for audit
    background_tasks.add_task(log_graphql_query, query, user_id=1)
    
    # Process query...

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 is GraphQL introspection dangerous in Fastapi production?
GraphQL introspection reveals your complete API schema including all types, mutations, and fields. In Fastapi production, this allows attackers to discover hidden endpoints, sensitive data structures, and potential vulnerabilities without any authentication. They can craft targeted attacks knowing exactly what your API accepts and returns, making exploitation much easier.
How can I test my Fastapi GraphQL API for introspection vulnerabilities?
Send a standard introspection query to your GraphQL endpoint and check if it returns schema information. You can also use middleBrick's automated scanner which tests GraphQL endpoints in under 15 seconds, identifies introspection exposure, and provides a security score with specific findings about schema exposure and sensitive fields.