HIGH xss cross site scriptingdjangohmac signatures

Xss Cross Site Scripting in Django with Hmac Signatures

Xss Cross Site Scripting in Django with Hmac Signatures — how this specific combination creates or exposes the vulnerability

Cross-site scripting (XSS) in Django when HMAC signatures are used typically arises from a mismatch between strong template escaping and developer assumptions that signed values are inherently safe. Django provides template auto-escaping and the escape filter, yet developers may mark signed data as safe or inject it into JavaScript or HTML attributes without re-escaping, bypassing protections.

Consider an endpoint that signs a user identifier using HMAC and embeds the signature in a page. If the application places the signature into a script context or an HTML attribute without proper escaping, an attacker who can influence the data portion of the signed payload may be able to break out and execute script. For example, using Django’s hmac module to sign a value and then rendering it with |safe or inside an onerror attribute can lead to reflected XSS even though the signature itself is cryptographically valid.

Another scenario involves JSON responses that include signed tokens. If a Django view embeds a signed value into a JSON block that is later interpreted as JavaScript (e.g., via a <script> tag or dynamic assignment), and the value is not properly encoded for that context, an attacker can inject executable code. This often happens when developers trust the signature’s integrity but neglect context-specific escaping rules defined by the HTML, JavaScript, and URL encoding standards.

Additionally, if an application signs user-controlled data that is later rendered in templates without escaping, the signature may validate while malicious content executes. Django’s template system escapes variables by default, but APIs that return signed data for client-side rendering must apply context-aware encoding (e.g., JavaScript string escaping) before inserting data into script blocks. Failure to do so turns a valid HMAC into a carrier for XSS, because the signature does not mitigate injection—it only verifies authenticity, not safety in the target context.

Hmac Signatures-Specific Remediation in Django — concrete code fixes

Remediation centers on ensuring that data and signatures are escaped according to the context where they are used. Never mark signed values as safe based solely on the integrity of the HMAC. Always apply appropriate escaping when rendering in HTML, attributes, JavaScript, or URLs.

Example 1: Safe HTML rendering with escaped signature

import hmac
import hashlib
from django.utils.html import escape
from django.http import HttpResponse

def my_view(request):
    user_id = '123'
    key = b'secret-key'
    signature = hmac.new(key, user_id.encode(), hashlib.sha256).hexdigest()
    # Safe: escaped for HTML body
    return HttpResponse(f'
User: {escape(user_id)}
')

Example 2: Safe attribute rendering with escaping

from django.utils.html import escape
from django.http import HttpResponse

def profile_view(request):
    username = 'alice'
    key = b'secret-key'
    signature = hmac.new(key, username.encode(), hashlib.sha256).hexdigest()
    # Safe: attribute values escaped
    return HttpResponse(f'Avatar')

Example 3: JSON response with context-aware encoding for JavaScript

import json
import hmac
import hashlib
from django.http import JsonResponse
from django.utils.html import escape

def api_token(request):
    token = 'sess-abc-123'
    key = b'secret-key'
    signature = hmac.new(key, token.encode(), hashlib.sha256).hexdigest()
    # Encode for safe JavaScript string context
    safe_token = escape(token).replace(''', '\\x27').replace('"', '\\x22')
    safe_sig = escape(signature).replace(''', '\\x27').replace('"', '\\x22')
    payload = {'token': safe_token, 'sig': safe_sig}
    return JsonResponse(payload, safe=False)

Example 4: Using Django’s built-in signing for tamper-proof data

from django.core.signing import TimestampSigner
from django.http import HttpResponse

def signed_view(request):
    signer = TimestampSigner()
    value = 'user-payload'
    signed = signer.sign(value)  # includes HMAC-based signature
    # Render escaped; do not use |safe
    return HttpResponse(f'Signed data')

Example 5: Template best practice

# views.py
from django.shortcuts import render
import hmac
import hashlib

def get_context(request):
    user_id = '42'
    key = b'secret-key'
    signature = hmac.new(key, user_id.encode(), hashlib.sha256).hexdigest()
    return {
        'user_id': user_id,
        'signature': signature,
    }

# template.html
<div data-sig="{{ signature|escape }}">User ID: {{ user_id|escape }}</div>

Related CWEs: inputValidation

CWE IDNameSeverity
CWE-20Improper Input Validation HIGH
CWE-22Path Traversal HIGH
CWE-74Injection CRITICAL
CWE-77Command Injection CRITICAL
CWE-78OS Command Injection CRITICAL
CWE-79Cross-site Scripting (XSS) HIGH
CWE-89SQL Injection CRITICAL
CWE-90LDAP Injection HIGH
CWE-91XML Injection HIGH
CWE-94Code Injection CRITICAL

Frequently Asked Questions

Does using HMAC signatures prevent XSS in Django templates?
No. HMAC ensures data integrity and authenticity but does not provide output encoding. Always escape signed values for the rendering context (HTML, attributes, JavaScript) to prevent XSS.
How should Django developers safely embed signed values in JavaScript?
Escape signed values for JavaScript string context (e.g., JSON-encode or apply JavaScript escapes) before embedding. Never inject raw signed data directly into script blocks or event handlers.