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'
')
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 ID | Name | Severity |
|---|---|---|
| CWE-20 | Improper Input Validation | HIGH |
| CWE-22 | Path Traversal | HIGH |
| CWE-74 | Injection | CRITICAL |
| CWE-77 | Command Injection | CRITICAL |
| CWE-78 | OS Command Injection | CRITICAL |
| CWE-79 | Cross-site Scripting (XSS) | HIGH |
| CWE-89 | SQL Injection | CRITICAL |
| CWE-90 | LDAP Injection | HIGH |
| CWE-91 | XML Injection | HIGH |
| CWE-94 | Code Injection | CRITICAL |