HIGH missing authenticationdjangohmac signatures

Missing Authentication in Django with Hmac Signatures

Missing Authentication in Django with Hmac Signatures — how this specific combination creates or exposes the vulnerability

HMAC signatures are commonly used in Django to verify the integrity and origin of requests, for example when consuming webhook payloads or authorizing internal service calls. If authentication is missing or incorrectly implemented around HMAC verification, an attacker can forge requests that appear valid.

Consider a Django view that expects an X-Signature header containing an HMAC-SHA256 of the payload, using a shared secret stored in settings:

import hmac
import hashlib
def verify_hmac(request):
    signature = request.META.get('HTTP_X_SIGNATURE')
    if signature is None:
        return False
    computed = hmac.new(
        key=settings.SHARED_SECRET.encode(),
        msg=request.body,
        digestmod=hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(computed, signature)

Missing authentication becomes a risk when the verification step is bypassed, skipped, or never enforced. For example, if a developer forgets to call verify_hmac for certain routes, or routes are configured to allow both authenticated and unauthenticated access, the HMAC check is effectively disabled. Another common pattern is using a weak or predictable shared secret, or storing it in source control, which undermines the purpose of HMAC.

Even when HMAC is present, missing authentication can manifest if the signature is computed over incomplete data. If the timestamp or nonce used to prevent replay attacks is not included in the HMAC input, an attacker can reuse a valid signature. In Django, this often occurs when developers sign only the JSON body and ignore query parameters or headers that influence server-side behavior.

Additionally, if the view performs permission checks based on user roles but skips HMAC validation for some HTTP methods (e.g., allowing GET without a signature while requiring it for POST), the unauthenticated attack surface expands. An attacker could leverage the unsigned path to probe endpoints or exfiltrate data that should otherwise be protected by request integrity checks.

Insecure default configurations can also contribute. If Django’s ALLOWED_HOSTS is too permissive or if HTTPS is not enforced in production, intercepted signatures may be reused. MiddleBrick scans detect these patterns by correlating endpoint behavior with the presence (or absence) of HMAC checks across the unauthenticated attack surface, highlighting authentication gaps that could enable unauthorized operations.

Hmac Signatures-Specific Remediation in Django — concrete code fixes

To secure Django endpoints using HMAC, ensure signature verification is mandatory, covers all relevant request data, and uses safe comparison. Below are concrete, working examples that address missing authentication and related weaknesses.

1. Enforce verification for all relevant methods and inputs

Always verify the HMAC for state-changing methods, and include query parameters and selected headers in the signed payload to prevent tampering:

import hmac
import hashlib
def verify_hmac_enforced(request):
    signature = request.META.get('HTTP_X_SIGNATURE')
    if signature is None:
        return False
    # Include query string to prevent parameter manipulation
    query_string = request.META.get('QUERY_STRING', '')
    payload = request.body
    if query_string:
        payload = payload + b'?' + query_string.encode()
    computed = hmac.new(
        key=settings.SHARED_SECRET.encode(),
        msg=payload,
        digestmod=hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(computed, signature)

from django.http import JsonResponse, HttpResponseBadRequest
def webhook_view(request):
    if request.method not in ('POST',):
        return HttpResponseBadRequest('Method not allowed')
    if not verify_hmac_enforced(request):
        return HttpResponseBadRequest('Invalid signature')
    # Process payload safely
    return JsonResponse({'status': 'ok'})

2. Use a constant-time comparison and protect against replay

Include a timestamp and nonce in the signed data, and validate freshness on the server to mitigate replay attacks:

import time
def verify_hmac_with_replay_protection(request):
    signature = request.META.get('HTTP_X_SIGNATURE')
    timestamp = request.META.get('HTTP_X_TIMESTAMP')
    nonce = request.META.get('HTTP_X_NONCE')
    if signature is None or timestamp is None or nonce is None:
        return False
    # Reject old requests (e.g., 5 minutes)
    if abs(time.time() - int(timestamp)) > 300:
        return False
    # Use a server-side cache or DB to track used nonces
    if cache.get(f'nonce:{nonce}'):
        return False
    payload = request.body
    computed = hmac.new(
        key=settings.SHARED_SECRET.encode(),
        msg=payload + b'|' + timestamp.encode() + b'|' + nonce.encode(),
        digestmod=hashlib.sha256
    ).hexdigest()
    if not hmac.compare_digest(computed, signature):
        return False
    cache.set(f'nonce:{nonce}', True, timeout=600)
    return True

3. Rotate secrets safely and avoid weak sources

Store secrets outside source code, for example using environment variables or a secrets manager, and rotate them periodically. In settings, load securely:

import os
from django.conf import settings
settings.SHARED_SECRET = os.environ.get('HMAC_SHARED_SECRET')
if not settings.SHARED_SECRET:
    raise ImproperlyConfigured('HMAC_SHARED_SECRET environment variable is required')

By combining enforced verification, inclusion of all request-affecting data, constant-time comparison, and replay protection, you eliminate the authentication gap that makes HMAC ineffective. MiddleBrick’s scans validate that signatures are checked across all methods and that findings map to frameworks such as OWASP API Top 10 and SOC2, providing prioritized remediation guidance without claiming to fix or block requests.

Related CWEs: authentication

CWE IDNameSeverity
CWE-287Improper Authentication CRITICAL
CWE-306Missing Authentication for Critical Function CRITICAL
CWE-307Brute Force HIGH
CWE-308Single-Factor Authentication MEDIUM
CWE-309Use of Password System for Primary Authentication MEDIUM
CWE-347Improper Verification of Cryptographic Signature HIGH
CWE-384Session Fixation HIGH
CWE-521Weak Password Requirements MEDIUM
CWE-613Insufficient Session Expiration MEDIUM
CWE-640Weak Password Recovery HIGH

Frequently Asked Questions

Why does including query parameters in the HMAC input matter for authentication in Django?
If query parameters influence server-side behavior but are excluded from the HMAC, an attacker can manipulate them to change the request meaning without invalidating the signature, bypassing authentication and integrity protections.
How often should the HMAC shared secret be rotated in a Django application?
Rotate secrets on a regular schedule (e.g., monthly or per incident) and immediately rotate if exposure is suspected. Store new secrets securely in environment variables or a secrets manager and redeploy configurations without committing secrets to source control.