HIGH api key exposuredjangomutual tls

Api Key Exposure in Django with Mutual Tls

Api Key Exposure in Django with Mutual Tls — how this specific combination creates or exposes the vulnerability

Django is a common host for services that require strong client authentication. Mutual Transport Layer Security (mTLS) uses client certificates to authenticate the client in addition to the server presenting a certificate. When mTLS is used, developers may assume the client certificate alone is sufficient and neglect other secrets such as API keys. This assumption can lead to exposure because an API key might still be required for authorization or to access downstream services, and those keys can be mishandled even when mTLS is in place.

In a Django-based API, an API key is often passed in an HTTP header (e.g., X-API-Key) or within request metadata. If the endpoint relies on mTLS for transport-layer identity but does not enforce strict validation of the key’s scope, origin, and revocation, an attacker who gains network visibility or compromises a client can misuse exposed keys. For example, logging mistakes may accidentally include the key in server logs or error messages, and weak key storage on the client side can lead to theft. The combination of mTLS and an API key can also create a false sense of security: mTLS ensures the client is who it claims to be, but it does not automatically prevent key leakage or guarantee that the key is used only for intended operations.

Another exposure vector arises when OpenAPI specs or integrations are shared across teams. If an API designed for mTLS clients publishes an OpenAPI definition that includes examples with real API keys, those keys can be exposed in documentation or repositories. Attackers scanning for exposed keys can exploit these findings even when mTLS is correctly configured. Moreover, if the Django app uses the API key to call external services without proper masking or redaction, the key can appear in outbound logs or third-party dashboards, increasing the risk of exfiltration.

Mutual Tls-Specific Remediation in Django — concrete code fixes

To reduce exposure risk, treat mTLS as one layer and apply defense-in-depth for API keys. Below are concrete practices and code examples for Django.

1. Configure mTLS in Django with proper client certificate validation

Ensure your Django settings enforce client certificate verification and validate the certificate chain. Use a robust SSL context that requires client certs and validates them against a trusted CA.

import ssl
from django.core.wsgi import get_wsgi_application

# Example of creating an SSL context that requires client certificates
ssl_context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
ssl_context.load_cert_chain(certfile="/path/to/server-cert.pem", keyfile="/path/to/server-key.pem")
ssl_context.load_verify_locations(cafile="/path/to/ca-chain.pem")
ssl_context.verify_mode = ssl.CERT_REQUIRED  # Enforce client cert validation

application = get_wsgi_application()

In production, terminate TLS at a load balancer or proxy (e.g., Nginx, Envoy) and forward the client certificate information to Django via headers such as SSL_CLIENT_CERT or HTTP_X_SSL_CLIENT_CERT. Validate the certificate fingerprint or subject against an allowlist in Django middleware to prevent unauthorized clients.

2. Secure API key handling independent of mTLS

Store API keys in environment variables or a secrets manager, never in code or version control. In Django, use os.getenv with a fallback that fails safely.

import os
from django.conf import settings

API_KEY = os.getenv("DOWNSTREAM_API_KEY")
if not API_KEY:
    raise RuntimeError("Missing required secret: DOWNSTREAM_API_KEY")

When sending requests to external services, avoid logging the key. Use request wrappers that scrub sensitive headers.

import requests

def call_external_service(headers):
    # Ensure the API key is not logged by the HTTP client or Django
    safe_headers = {k: v for k, v in headers.items() if k.lower() != "x-api-key"}
    # Pass the key separately via params or Authorization as required by the service
    key = headers.get("X-API-Key")
    response = requests.get(
        "https://api.example.com/data",
        headers=safe_headers,
        params={"api_key": key} if key else None,
        timeout=5,
    )
    response.raise_for_status()
    return response.json()

3. Map mTLS identities to authorization, not just authentication

Use the client certificate details (e.g., subject, issuer, serial) to enrich request metadata and apply fine-grained permissions. Do not rely solely on the presence of a certificate to authorize sensitive operations.

from django.http import HttpRequest, HttpResponseForbidden

def validate_client_certificate(request: HttpRequest) -> bool:
    cert_pem = request.META.get("SSL_CLIENT_CERT")
    if not cert_pem:
        return False
    # Perform validation: parse cert, check CN/OUs, verify against allowlist
    # This is a placeholder for actual parsing logic (e.g., using cryptography)
    return True

def my_view(request):
    if not validate_client_certificate(request):
        return HttpResponseForbidden("Invalid client certificate")
    # Proceed with business logic, using API keys scoped to the client identity
    return HttpResponse("OK")

4. Rotate and revoke keys independently of certificates

Implement a process to rotate API keys on a schedule and to revoke compromised keys without relying on certificate revocation alone. Use short-lived tokens where possible and tie key validity to the client certificate metadata.

5. Scan dependencies and documentation for exposed keys

Use scanning tools to detect hardcoded keys in code and to audit OpenAPI specs before publishing. Ensure examples in documentation use placeholder values and that CI checks prevent accidental commits of secrets.

Frequently Asked Questions

Does mTLS eliminate the need for API keys in Django?
No. mTLS provides strong client authentication at the transport layer, but API keys may still be required for authorization, auditing, or calling downstream services. Treat mTLS as one layer and continue to protect API keys separately.
How can I detect accidental API key exposure in Django logs when using mTLS?
Instrument logging to redact or omit sensitive headers (e.g., X-API-Key). Use middleware to scrub request data before it reaches logging pipelines and audit logs regularly for key patterns.