HIGH graphql introspectiondjangohmac signatures

Graphql Introspection in Django with Hmac Signatures

Graphql Introspection in Django with Hmac Signatures — how this specific combination creates or exposes the vulnerability

GraphQL introspection in Django allows clients to query the schema for types, queries, and mutations. When combined with HMAC signatures used for request authentication, a misconfiguration can expose the schema even when signatures are expected. HMAC signatures are typically computed over a canonical representation of the request (e.g., HTTP method, path, timestamp, and body) and verified server-side before processing.

If introspection queries are permitted for signed requests but the signature verification does not explicitly exclude or handle introspection operations, an attacker can probe the endpoint with introspection payloads while replaying or slightly altering the HMAC to test acceptance. Because introspection queries are read-only and often bated with other operations, a weak HMAC implementation may accept them without requiring additional scope or elevated permissions, effectively bypassing intended access control.

Consider a Django view that expects a JSON payload with a timestamp, signature, and query. If the view verifies the HMAC over the timestamp and query string but allows introspection queries to proceed after verification, an unauthenticated attacker can send an introspection request with a valid HMAC derived from a known timestamp and query structure. This reveals the full schema, including sensitive types and fields, which may include references to internal models or business logic not intended for public exposure.

In practice, this becomes a two-stage issue: first, the attacker obtains the schema via introspection; second, they craft targeted queries or IDOR attempts using the discovered field names. Because the HMAC is tied to the request body, minor changes to whitespace or field ordering can break the signature, so attackers often rely on automated replay of captured introspection requests to confirm acceptance. The combination of open introspection and signature-based authentication without strict scoping increases the risk of information leakage and subsequent abuse.

Hmac Signatures-Specific Remediation in Django — concrete code fixes

To secure GraphQL introspection in Django when using HMAC signatures, explicitly gate introspection behind authentication or scope checks, and ensure signature verification rejects introspection queries unless explicitly permitted. Below is a concrete example of a Django view that verifies HMAC and blocks introspection unless an additional flag or role is present.

import hashlib
import hmac
import json
import time
from django.http import JsonResponse
from django.views.decorators.http import require_http_methods

SECRET_KEY = b'your-secure-secret-key'
ALLOW_INTROSPECTION = False  # Set to True only in controlled environments

def verify_hmac(data, received_signature):
    computed = hmac.new(SECRET_KEY, data.encode('utf-8'), hashlib.sha256).hexdigest()
    return hmac.compare_digest(computed, received_signature)

@require_http_methods(["POST"])
def graphql_view(request):
    try:
        body = request.body.decode('utf-8')
        payload = json.loads(body)
    except (ValueError, json.JSONDecodeError):
        return JsonResponse({'error': 'Invalid JSON'}, status=400)

    timestamp = payload.get('timestamp')
    signature = payload.get('signature')
    query = payload.get('query', '')

    if not timestamp or not signature:
        return JsonResponse({'error': 'Missing timestamp or signature'}, status=400)

    # Reject old requests to prevent replay (e.g., 5-minute window)
    if abs(time.time() - int(timestamp)) > 300:
        return JsonResponse({'error': 'Timestamp expired'}, status=400)

    # Canonical data for HMAC: timestamp + query
    canonical = f'{timestamp}{query}'
    if not verify_hmac(canonical, signature):
        return JsonResponse({'error': 'Invalid signature'}, status=403)

    # Block introspection unless explicitly allowed
    if 'Introspection' in query and not ALLOW_INTROSPECTION:
        return JsonResponse({'error': 'Introspection not permitted'}, status=403)

    # Here, pass the query to your GraphQL executor
    # result = execute_graphql(query)
    return Json_response({'data': {'ok': True}})

In this pattern, the HMAC is computed over the concatenated timestamp and query string, ensuring that any modification to the query—such as replacing the operation with an introspection query—invalidates the signature unless the server’s ALLOW_INTROSPECTION flag is set to True. This approach aligns with the principle of least privilege: by default, introspection is denied, and only explicitly permitted scenarios can proceed.

Additionally, pair this with Django middleware that rejects introspection at the framework level when authentication scopes are enforced. For example, use a custom decorator that inspects the parsed AST of the GraphQL query before execution and raises a permission error if introspection is detected and the request lacks the required scope.

from graphql import parse

INTROSPECTION_QUERY = 'IntrospectionQuery'

def require_scope(view_func):
    def wrapper(request, *args, **kwargs):
        body = json.loads(request.body.decode('utf-8'))
        query = body.get('query', '')
        document = parse(query)
        for definition in document.definitions:
            if definition.__class__.__name__ == INTROSPECTION_QUERY:
                if not request.user.has_scope('introspect'):
                    return JsonResponse({'error': 'Insufficient scope'}, status=403)
        return view_func(request, *args, **kwargs)
    return wrapper

These examples demonstrate how to tightly couple HMAC verification with introspection policy in Django, ensuring that schema discovery remains controlled and auditable.

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

Can HMAC signatures prevent all GraphQL introspection risks?
No. HMAC signatures protect integrity and authenticity of requests but do not inherently block introspection. You must explicitly reject or gate introspection queries in application logic.
What is a safer alternative to public introspection in Django GraphQL endpoints?
Disable introspection in production by removing the introspection field from the GraphQL schema or by using a custom middleware that rejects introspection queries unless the caller has explicit scope or elevated privileges.