MEDIUM graphql introspectiondjangomutual tls

Graphql Introspection in Django with Mutual Tls

Graphql Introspection in Django with Mutual Tls — how this specific combination creates or exposes the vulnerability

GraphQL introspection in Django, when combined with mutual TLS (mTLS), can expose information that an authenticated endpoint might otherwise protect. Introspection allows clients to query the GraphQL schema, including types, queries, and mutations, which is useful during development but can aid an attacker in mapping the API surface. In a Django GraphQL setup using packages such as graphene-django or Strawberry, introspection is often enabled by default in non-production settings. Even when mTLS is enforced at the transport layer to authenticate clients, introspection can still reveal details about object models, field relationships, and resolver logic that are not directly exposed through schema design.

Mutual TLS ensures that both client and server present certificates, which strongly authenticates the client to the Django application. However, mTLS does not limit what the authenticated client can query once the TLS handshake completes. If introspection is allowed for mTLS-authenticated requests, an attacker who obtains a valid client certificate can still perform schema queries to explore endpoints, field arguments, and response shapes. This becomes a concern when introspection is unintentionally permitted in production or when certificate-based access control does not align with GraphQL authorization rules.

The combination can inadvertently widen the attack surface: mTLS secures the channel, but if introspection is not explicitly restricted, it provides a structured map of the API to any client that presents a valid certificate. This is particularly relevant in microservice environments where mTLS is common and GraphQL endpoints are exposed internally. Moreover, introspection responses may include details about input types and default values that can be leveraged in further attacks, such as crafting malicious queries or probing for business logic flaws.

To evaluate risk, you can scan the endpoint with middleBrick, which checks unauthenticated and authenticated attack surfaces, including GraphQL-specific exposures where applicable. Its LLM/AI Security checks can also detect whether introspection responses might leak information that could be used in prompt injection or data exfiltration scenarios.

Mutual Tls-Specific Remediation in Django — concrete code fixes

To secure GraphQL introspection in Django when using mutual TLS, you should disable introspection for production or limit it to specific internal clients, and enforce strict client certificate mapping to views. Below are concrete code examples for configuring mTLS in Django and controlling introspection behavior.

Mutual TLS setup in Django

Use django-sslserver or a reverse proxy (such as Nginx or Traefik) to handle client certificate verification, and pass the verified client certificate information to Django via headers. Ensure your Django settings validate the certificate chain and extract the client identity.

import ssl
from pathlib import Path

# settings.py
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')

# Assume the reverse proxy sets these headers after verifying client certs
CLIENT_CERT_HEADER = 'SSL_CLIENT_S_DN_CN'  # Example header from proxy

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'yourapp.middleware.ClientCertificateMiddleware',  # Custom middleware
]

Custom middleware to enforce client certificate validation

This middleware checks that a required header (populated by the proxy after mTLS verification) is present and optionally maps the certificate to an allowed list of subjects.

import logging
from django.http import HttpResponseForbidden

logger = logging.getLogger(__name__)

class ClientCertificateMiddleware:
    def __init__(self, get_response):
        self.get_response = get_response

    def __call__(self, request):
        cert_subject = request.META.get('HTTP_X_SSL_CLIENT_DN')
        if not cert_subject:
            logger.warning('Missing client certificate')
            return HttpResponseForbidden('Client certificate required')
        # Optionally validate against an allowlist
        allowed_subjects = {'CN=internal-client-1', 'CN=internal-client-2'}
        if cert_subject not in allowed_subjects:
            logger.warning(f'Unauthorized client certificate: {cert_subject}')
            return HttpResponseForbidden('Client certificate not authorized')
        request.client_subject = cert_subject
        return self.get_response(request)

Disabling or restricting introspection in GraphQL views

In your GraphQL view, conditionally disable introspection based on the request or client identity. With graphene-django, you can pass graphiql=False and control the schema introspection option. With Strawberry, use environment-based flags or request checks.

from django.views.decorators.csrf import csrf_exempt
from graphene_django.views import GraphQLView

class SecureGraphQLView(GraphQLView):
    def get_response(self, request, data, show_graphiql=False):
        # Disable introspection for non-internal clients
        if not getattr(request, 'client_subject', '').startswith('CN=internal'):
            # Force schema to exclude introspection fields if supported
            # This pattern depends on your GraphQL library's capabilities
            data['query'] = self.mask_introspection_query(data.get('query', ''))
        return super().get_response(request, data, show_graphiql=False)

    def mask_introspection_query(self, query):
        # Simple safeguard: remove introspection fields for non-allowed clients
        import re
        if query and re.search(r'\b(introspection|__schema|__type)\b', query):
            return '{ __typename }'  # Return a minimal safe response
        return query

urlpatterns = [
    path('graphql/', csrf_exempt(SecureGraphQLView.as_view(graphiql=False))),
]

For Strawberry, you can gate introspection behind a runtime check:

import strawberry
from strawberry.http import GraphQLRequest
from django.conf import settings

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

schema = strawberry.Schema(query=Query)

@csrf_exempt
def graphql_view(request):
    if request.method == 'OPTIONS':
        return HttpResponse('OK')
    body = request.body if hasattr(request, 'body') else b''
    # Only allow introspection for trusted client certificates
    if 'introspection' in body.decode('utf-8', errors='ignore') and not request.META.get('HTTP_X_SSL_CLIENT_DN', '').startswith('CN=internal'):
        return HttpResponse('Introspection not allowed', status=403)
    return schema.execute_sync(
        request.body.decode('utf-8'),
        context_value={'request': request},
    )

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

Does mutual TLS alone prevent GraphQL introspection abuse?
No. Mutual TLS authenticates the client but does not restrict what queries the client can execute. You must explicitly disable or gate introspection in your GraphQL views and align client certificate mapping with authorization rules.
How can I verify my Django GraphQL endpoint is not leaking schema details over mTLS?
Use middleBrick to scan the endpoint and review findings related to introspection and information exposure. Combine this with manual checks that introspection is disabled or restricted for non-internal clients in production.