Graphql Introspection in Django with Bearer Tokens
Graphql Introspection in Django with Bearer Tokens — how this specific combination creates or exposes the vulnerability
GraphQL introspection in Django, when combined with bearer token authentication, can unintentionally expose the full schema and internal types even when bearer protection is present. This occurs because introspection queries are typically processed before or independently of authorization checks in many GraphQL-for-Python setups, allowing an unauthenticated or partially authenticated attacker to discover types, queries, and field names that should be guarded.
In a Django GraphQL implementation, introspection is often enabled in development and sometimes left enabled in production behind an API gateway or middleware that validates bearer tokens at the HTTP layer. If the GraphQL view does not explicitly disable introspection or enforce authorization within the resolver layer, an attacker can send an introspection query with a valid bearer token (or sometimes without one) and retrieve the complete schema. This information can reveal sensitive data models, query patterns, and potential injection surfaces, aiding further attacks such as BOLA/IDOR or injection.
When bearer tokens are used, the expectation is that only clients with a valid token can proceed. However, if introspection is not explicitly disabled, the token may grant broad visibility into the API structure. For example, a token issued for a limited scope might still allow introspection, exposing fields that should be restricted to certain roles. Attackers can leverage this to map the API surface and identify endpoints or mutations that accept user input, which can then be tested for injection or authorization flaws.
Consider a typical Django GraphQL view using graphene-django or strawberry:
from django.http import HttpRequest
from graphene_django.views import GraphQLView
class BearerGraphQLView(GraphQLView):
def get_response(self, request: HttpRequest, data):
# Insecure: introspection allowed regardless of token validity or scope
return super().get_response(request, data)
In this pattern, if the view does not validate the token before allowing introspection, an attacker with any valid bearer token can retrieve the schema. Even with token validation at the middleware level, if introspection is not explicitly blocked, the schema is exposed.
To align with the checks performed by middleBrick, this scenario is flagged under the Authentication and Property Authorization categories. The scanner tests whether introspection can be executed with different token states and whether the schema is leaked, mapping findings to relevant compliance frameworks such as OWASP API Top 10 and SOC2.
Bearer Tokens-Specific Remediation in Django — concrete code fixes
To secure GraphQL introspection in Django when using bearer tokens, explicitly disable introspection in production and enforce authorization within the GraphQL layer. This ensures that even with a valid token, introspection is not available unless explicitly permitted for trusted clients.
One approach is to override the introspection field in your schema and gate it behind role or scope checks. Below is a strawberry example that disables introspection by default and allows it only for an internal monitoring token:
import strawberry
from typing import Optional
# Example: allow introspection only for a special internal token
INTROSPECTION_TOKEN = "internal-monitoring-token-123"
@strawberry.type
class Query:
@strawberry.field
def hello(self) -> str:
return "world"
@strawberry.type
class Mutation:
@strawberry.mutation
def set_value(self, value: str) -> bool:
return True
@strawberry.schema
class MySchema:
query: Query
mutation: Mutation
# Override introspection to enforce custom logic
@property
def __type_map__(self):
# In production, remove or restrict introspection entries
if not self._introspection_allowed():
return {}
return super().__type_map__
def _introspection_allowed(self) -> bool:
# In real usage, inspect request context; here we simulate with env/headers
import os
return os.environ.get("ALLOW_INTROSPECTION") == "true"
In a graphene-django project, you can disable introspection at the view level by customizing the GraphQL view:
from django.http import HttpRequest
from graphene_django.views import GraphQLView
class SecureGraphQLView(GraphQLView):
def get_response(self, request: HttpRequest, data):
# Extract bearer token from Authorization header
auth = request.META.get("HTTP_AUTHORIZATION", "")
token = None
if auth and auth.startswith("Bearer "):
token = auth.split(" ", 1)[1]
# Allow introspection only for a known internal token
if data and data.get("query", "").strip().startswith("__schema"):
if token != "internal-monitoring-token-123":
return {"errors": [{"message": "Introspection not allowed"}]}
return super().get_response(request, data)
Additionally, ensure that bearer token validation occurs before the GraphQL resolver chain. Use Django middleware to attach user context and enforce scope-based checks so that even if introspection is allowed in limited cases, the fields returned respect token scopes.
For operational monitoring, the middleBrick CLI can be used to verify that introspection is properly restricted:
middlebrick scan https://api.example.com/graphql
Using the GitHub Action, you can fail builds if the risk score exceeds your threshold, preventing schema exposure in deployment pipelines. The MCP server integration allows you to scan APIs directly from your AI coding assistant while developing these fixes.
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 |