HIGH clickjackingdjangohmac signatures

Clickjacking in Django with Hmac Signatures

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

Clickjacking relies on embedding a target site inside an invisible or disguised frame so that user interactions are hijacked. Django provides several defenses, including X-Frame-Options and Content-Security-Policy frame-ancestors, but if Hmac Signatures are used to protect state-modifying URLs (e.g., a confirmation or action link that includes an HMAC of the user ID, timestamp, and action), developers may incorrectly assume the page is safe even when embedded.

Consider a link like /transfer?amount=100&to=attacker&ts=1700000000&hmac=abc123. The HMAC ensures the parameters have not been tampered with, but it does not prevent the page from being loaded inside a malicious site via an iframe. An attacker can trick a logged-in user into visiting a page that contains an invisible form or image request that navigates the top-level window to that signed URL. Because the browser sends cookies automatically, the signed request is executed in the user’s context, and the HMAC validation passes, leading to an unwanted action (a BOLA/IDOR-style effect via signed navigation).

This specific combination is risky when Hmac Signatures protect actions but the same-origin policy and frame protections are not enforced consistently. For example, if Django does not set X-Frame-Options or Content-Security-Policy frame-ancestors to DENY or SAMEORIGIN, an attacker can embed the page in an iframe and overlay UI elements to induce a click. The HMAC does not protect against the page being rendered in an unintended context; it only protects parameter integrity. Therefore, clickjacking remains possible because framing controls are orthogonal to HMAC validation.

Additionally, if the signed URL is embedded inside an iframe on an attacker-controlled page and the user is tricked into interacting with invisible controls that submit the URL via JavaScript, the signature remains valid but the user’s intent is subverted. This is especially dangerous for sensitive operations like changing email, disabling 2FA, or performing financial transfers, where the signature proves legitimacy but the framing bypasses user consent.

Hmac Signatures-Specific Remediation in Django — concrete code fixes

To mitigate clickjacking when using Hmac Signatures in Django, combine signature integrity with strong framing policies and careful view design. Below are concrete steps and code examples.

1. Enforce framing controls at the Django level

Set HTTP headers so that the browser refuses to render sensitive pages in frames.

from django.middleware.clickjacking import XFrameOptionsMiddleware

# settings.py (ensure it's included in MIDDLEWARE)
MIDDLEWARE = [
    ...
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
    ...
]

# Optional: set default X-Frame-Options for older browsers
X_FRAME_OPTIONS = 'DENY'

For modern browsers, use Content-Security-Policy frame-ancestors instead:

from django.middleware.security import SecurityMiddleware

# settings.py
SECURE_CSP_FRAME_ANCESTORS = ("'none'",)  # or ("'self'",) for same-origin frames

2. Use Django’s built-in django.middleware.clickjacking decorators

Apply xframe_options_exempt only where embedding is intentional, and otherwise rely on default protections.

from django.views.decorators.clickjacking import xframe_options_deny, xframe_options_sameorigin, xframe_options_exempt

@xframe_options_deny
def sensitive_action_view(request):
    ...

3. Hmac Signatures example with integrity and short expiry

Generate and validate signed URLs to prevent tampering, and pair them with CSRF tokens for state-changing POSTs.

import base64
import hashlib
import hmac
import time
from urllib.parse import urlencode

from django.conf import settings
from django.http import HttpResponseRedirect
from django.urls import reverse

def build_signed_url(base_url, params, secret_key, expiry_seconds=300):
    """Return base_url?{params}&hmac=..."""
    params = dict(params, ts=int(time.time()))
    qs = urlencode(params, doseq=True)
    message = qs.encode('utf-8')
    signature = hmac.new(
        secret_key.encode('utf-8'),
        message,
        hashlib.sha256
    ).digest()
    params['hmac'] = base64.urlsafe_b64encode(signature).rstrip(b'=').decode('ascii')
    return f"{base_url}?{urlencode(params, doseq=True)}"

def validate_signed_url(request):
    params = request.GET.dict()
    provided_hmac = params.pop('hmac', None)
    if provided_hmac is None:
        raise SuspiciousOperation('Missing HMAC')
    ts = int(params.get('ts', 0))
    if abs(time.time() - ts) > 300:  # 5-minute window
        raise SuspiciousOperation('Expired signature')
    secret_key = settings.SECRET_KEY
    message = urlencode(params, doseq=True).encode('utf-8')
    expected = base64.urlsafe_b64encode(hmac.new(
        secret_key.encode('utf-8'), message, hashlib.sha256
    ).digest()).rstrip(b'=').decode('ascii')
    if not hmac.compare_digest(provided_hmac, expected):
        raise SuspiciousOperation('Invalid HMAC')
    # proceed with action
    return params

4. Defense in depth: combine with CSRF and SameSite cookies

Use SameSite=Lax or Strict for session cookies and include CSRF tokens in any state-changing POST requests, even when a signed GET is used to initiate the flow.

# settings.py
CSRF_COOKIE_SAMESITE = 'Strict'
SESSION_COOKIE_SAMESITE = 'Lax'

For GET-based actions that must be safe, ensure they are idempotent and do not change state without additional verification (e.g., require a POST with CSRF token after the signed GET confirms intent).

Frequently Asked Questions

Do Hmac Signatures prevent clickjacking on their own?
No. Hmac Signatures protect parameter integrity and tampering, but they do not prevent a page from being embedded in an iframe or prevent user interactions from being hijacked. You must also set X-Frame-Options or Content-Security-Policy frame-ancestors to control framing.
Is it enough to set X-Frame-Options and ignore Hmac Signatures validation for framed contexts?
You should still validate Hmac Signatures to ensure the request parameters have not been altered. X-Frame-Options and CSP frame-ancestors prevent framing, but Hmac Signatures protect against tampered query parameters in scenarios where framing controls might be bypassed or misconfigured.