HIGH clickjackingdjango

Clickjacking in Django

How Clickjacking Manifests in Django — specific attack patterns, Django-specific code paths where this appears

Clickjacking leverages UI redressing by embedding a target site in an invisible or disguised frame. In Django, common patterns include missing or misconfigured Content Security Policy (CSP) frame-ancestors, absent X-Frame-Options, and templates that render forms without anti-CSRF token placement in all contexts. Attackers embed the Django admin or custom views in an <iframe> and overlay transparent controls to trick users into performing unwanted actions.

Django-specific code paths where clickjacking risk arises include views relying on default django.middleware.clickjacking.XFrameOptionsMiddleware with a permissive setting, class-based views that omit frame restrictions, and templates that render sensitive forms without ensuring a strict frame ancestor policy. For example, an admin view exposed at /admin/ may be inadvertently embeddable if X_FRAME_OPTIONS is set to ALLOW-FROM or not configured, enabling an attacker to load https://example.com/admin/change-form/ inside a malicious page. Similarly, custom views using TemplateResponse or render without explicitly setting X-Frame-Options or CSP frame-ancestors can be targeted.

Consider a Django view that renders a sensitive confirmation form:

from django.shortcuts import render
from django.views import View

class SensitiveActionView(View):
    def get(self, request):
        return render(request, 'confirm.html', {'action': 'delete'})

If this view does not enforce frame restrictions, an attacker can load it inside an invisible frame and overlay a fake UI that submits the form. Even when XFrameOptionsMiddleware is enabled, a permissive configuration such as X_FRAME_OPTIONS = 'ALLOWALL' or a custom X-Frame-Options: ALLOW-FROM https://example.com that is not enforced by browsers leaves the endpoint vulnerable. In single-page setups where Django serves an API-backed frontend, embedding admin or action endpoints in iframes via JavaScript also increases exposure.

Django-Specific Detection — how to identify this issue, including scanning with middleBrick

Detecting clickjacking risk in Django involves inspecting HTTP response headers and CSP directives. Key indicators are missing or weak X-Frame-Options values (e.g., absent, ALLOWALL, or deprecated ALLOW-FROM) and a lack of a restrictive Content-Security-Policy header with frame-ancestors. Developers can manually test by loading a view in a test page with an invisible iframe and observing whether the browser blocks the load; however, this is error-prone and incomplete.

Automated scanners like middleBrick perform black-box checks that validate header presence and values, and they correlate findings with the OpenAPI spec to highlight endpoints that expose admin or sensitive actions without frame restrictions. For example, a scan can detect that an endpoint like /admin/login/ lacks X-Frame-Options or a strong CSP frame-ancestors directive, surfacing it as a potential clickjacking vector. middleBrick runs 12 security checks in parallel, including Input Validation and Property Authorization, which can reveal forms that are not adequately protected in the rendering pipeline, complementing header-based findings.

Using the CLI, you can quickly assess a Django endpoint:

$ middlebrick scan https://api.example.com/admin/login/
{ 
  "url": "https://api.example.com/admin/login/",
  "score": 52,
  "grade": "D",
  "findings": [
    { 
      "check": "Clickjacking",
      "severity": "high",
      "description": "Missing or permissive X-Frame-Options; CSP frame-ancestors not restrictive",
      "remediation": "Set X_FRAME_OPTIONS = 'DENY' globally or per-view, and configure Content-Security-Policy frame-ancestors to "'self'" or specific origins."
    }
  ]
}

In the Web Dashboard, you can track these findings over time and see per-category breakdowns, which helps prioritize remediation for high-risk endpoints such as authentication flows and admin interfaces.

Django-Specific Remediation — code fixes using Django's native features/libraries

Remediation centers on enforcing strict frame-embedding policies via headers and CSP. The simplest approach is to rely on Django’s built-in XFrameOptionsMiddleware and configure it appropriately. For project-wide protection, set X_FRAME_OPTIONS = 'DENY' in settings.py, which ensures that no page can be framed. For cases where limited embedding is required, use X_FRAME_OPTIONS = 'SAMEORIGIN' to allow framing only by pages on the same origin.

For modern CSP compliance, define a Content-Security-Policy header with a restrictive frame-ancestors directive. This can be done via middleware or a web server, but within Django you can use the csp package or a custom middleware to set headers precisely per view.

Example settings-based enforcement:

# settings.py
X_FRAME_OPTIONS = 'DENY'
# For CSP, use a package like django-csp:
CSP_DEFAULT_SRC = ["'self'"]
CSP_FRAME_ANCESTORS = ["'self'"]

Per-view enforcement is useful when some endpoints may be embeddable. With class-based views, set headers in dispatch or use a mixin:

from django.utils.decorators import method_decorator
from django.views import View
from django.middleware.clickjacking import xframe_options_exempt, xframe_options_deny
from django.views.decorators.clickjacking import xframe_options_exempt, xframe_options_deny

class PublicPageView(View):
    @method_decorator(xframe_options_exempt)
    def dispatch(self, *args, **kwargs):
        return super().dispatch(*args, **kwargs)

class AdminActionView(View):
    @method_decorator(xframe_options_deny)
    def dispatch(self, *args, **kwargs):
        return super().dispatch(*args, **kwargs)

For function-based views, apply decorators directly:

from django.views.decorators.clickjacking import xframe_options_deny
from django.shortcuts import render

@xframe_options_deny
def sensitive_action(request):
    return render(request, 'confirm.html', {'action': 'delete'})

When using Django REST Framework, add a custom middleware or override initialize_request to ensure that API responses include the same headers. Combining X_FRAME_OPTIONS with a strict CSP frame-ancestors provides defense in depth, mitigating clickjacking even if one layer is misconfigured. Regular scans with middleBrick Pro can validate that these headers are present and correctly configured across all endpoints.

Frequently Asked Questions

Does setting X_FRAME_OPTIONS = 'DENY' break legitimate embedded widgets in Django?
Yes, setting X_FRAME_OPTIONS to 'DENY' prevents any page from being embedded in an iframe. If you need to embed widgets, use 'SAMEORIGIN' or a strict Content-Security-Policy frame-ancestors directive that explicitly allows trusted origins, and validate with scans to ensure only intended endpoints are embeddable.
Can middleBrick detect clickjacking risks in Django REST Framework APIs that serve JSON only?
middleBrick checks response headers regardless of response format. Even if a Django REST Framework endpoint returns JSON, missing or weak X-Frame-Options and CSP headers are flagged. Since embedding JSON endpoints in frames is uncommon, the presence of these headers still reduces risk and aligns with defense-in-depth.