HIGH clickjackingdjangobearer tokens

Clickjacking in Django with Bearer Tokens

Clickjacking in Django with Bearer Tokens — how this specific combination creates or exposes the vulnerability

Clickjacking is a client-side UI redress attack where an invisible or disguised frame tricks a user into interacting with a page they did not intend to interact with. In Django, APIs that rely on Bearer Tokens for authentication can still be exposed to clickjacking when token handling is combined with pages that perform sensitive actions via GET requests or render forms without proper anti-CSRF protections.

Consider an API endpoint that accepts a Bearer Token in the Authorization header and also provides a page that includes JavaScript to automatically make authenticated requests. If that page embeds third-party content or is itself embedded in an attacker-controlled frame, an attacker can overlay UI elements to capture user input or trigger unintended API calls. Even when Bearer Tokens are used, browsers send credentials (including Authorization headers) with same-origin requests, so an embedded malicious form or script can cause the browser to perform actions on behalf of the authenticated user.

In a typical Django setup using token-based authentication, views might parse tokens from headers but neglect to enforce frame-busting or Content Security Policy (CSP) frame-ancestors rules. Without explicit X-Frame-Options or CSP frame-ancestors directives, the API’s web interface or any page that uses JavaScript to call the Bearer-token-protected endpoints can be loaded inside an iframe. An attacker can position invisible submit buttons or links over these iframes to induce clicks on privileged operations such as changing settings or initiating transactions.

Moreover, if Django templates render forms that rely on Bearer Tokens stored in JavaScript variables or in cookies without the SameSite and Secure attributes, the risk increases. Browsers will include cookies automatically, and if a CSRF token is missing or not validated for state-changing requests, clickjacking can lead to unauthorized actions. Even when using Bearer Tokens in headers, developers sometimes implement endpoints that accept unsafe methods like GET for convenience, which can be triggered via an embedded image or script from an attacker site, leading to unintended data exposure or changes.

To understand the impact, imagine a Django view that reads a token from the Authorization header and performs a money transfer based on query parameters without verifying the request origin. If an attacker crafts a page with an invisible form that submits to this view, and the view does not validate Referer or enforce CSRF protection for token-based requests, the victim’s browser will send the token along with the request, completing the transfer without user consent. This demonstrates how Bearer Tokens alone do not prevent clickjacking; complementary defenses are required.

Bearer Tokens-Specific Remediation in Django — concrete code fixes

Remediation focuses on preventing embedding, enforcing CSRF where applicable, and ensuring tokens are handled securely in both browser and non-browser contexts. Below are concrete code examples for Django views and settings.

1. Set X-Frame-Options and Content Security Policy

Ensure responses include headers that prevent framing. In Django settings, add security middleware or use built-in configurations.

class XFrameOptionsMiddleware:
    def __init__(self, get_response):
        self.get_response = get_response

    def __call__(self, request):
        response = self.get_response(request)
        response['X-Frame-Options'] = 'DENY'
        return response

# In settings.py, ensure CSP frame-ancestors is set via a middleware or security library
# Example using django-csp (install via pip install django-csp):
# CSP_DEFAULT_SRC = ("'self'",)
# CSP_FRAME_ANCESTORS = ("'none'",)

2. Use SameSite and Secure Cookie Attributes for Session Tokens

If your Bearer Tokens are stored in cookies (e.g., for web-based clients), configure Django to protect them.

# settings.py
SESSION_COOKIE_SAMESITE = 'Strict'
SESSION_COOKIE_SECURE = True  # Use HTTPS in production
CSRF_COOKIE_SAMESITE = 'Strict'
CSRF_COOKIE_SECURE = True

3. Validate Origin and Referer Headers for Sensitive Actions

Add checks in views that perform state-changing operations, even when using Bearer Tokens.

import os
from django.http import HttpResponseForbidden

def validate_request_origin(request):
    allowed_origin = os.getenv('ALLOWED_ORIGIN', 'https://yourdomain.com')
    origin = request.META.get('HTTP_ORIGIN')
    referer = request.META.get('HTTP_REFERER')
    if origin and origin != allowed_origin:
        return False
    if referer and not referer.startswith('https://yourdomain.com'):
        return False
    return True

def sensitive_action(request):
    if not validate_request_origin(request):
        return HttpResponseForbidden('Invalid request origin')
    # Proceed with token-based logic
    auth_header = request.headers.get('Authorization')
    if not auth_header or not auth_header.startswith('Bearer '):
        return HttpResponseForbidden('Missing or invalid token')
    token = auth_header.split(' ')[1]
    # Perform action
    return HttpResponse('Action completed')

4. Avoid GET for State-Changing Operations

Ensure endpoints that modify data use POST, PUT, or DELETE and require explicit CSRF tokens or custom header validation.

from django.views.decorators.csrf import csrf_protect
from django.http import JsonResponse

@csrf_protect
def transfer_funds(request):
    if request.method != 'POST':
        return JsonResponse({'error': 'Method not allowed'}, status=405)
    auth_header = request.headers.get('Authorization')
    if not auth_header or not auth_header.startswith('Bearer '):
        return JsonResponse({'error': 'Unauthorized'}, status=401)
    token = auth_header.split(' ')[1]
    # Validate token and perform transfer
    return JsonResponse({'status': 'success'})

5. Use Django REST Framework with Token Authentication Safely

When using DRF, ensure session authentication is disabled for API views and that permissions enforce safe framing.

from rest_framework.views import APIView
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from rest_framework import status

class SecureAPIView(APIView):
    permission_classes = [IsAuthenticated]

    def dispatch(self, request, *args, **kwargs):
        # Ensure no session-based CSRF bypass for token auth
        if not request.headers.get('Authorization', '').startswith('Bearer '):
            return Response({'detail': 'Unauthorized'}, status=status.HTTP_401_UNAUTHORIZED)
        return super().dispatch(request, *args, **kwargs)

    def post(self, request):
        # Action logic
        return Response({'result': 'ok'})

6. Middleware for Clickjacking Protection

Implement a simple middleware to strip tokens from Referer headers in cross-origin requests.

class ReferrerPolicyMiddleware:
    def __init__(self, get_response):
        self.get_response = get_response

    def __call__(self, request):
        response = self.get_response(request)
        response['Referrer-Policy'] = 'no-referrer'
        response['Permissions-Policy'] = 'geolocation=()'
        return response

Frequently Asked Questions

Can clickjacking occur if Django APIs use only Bearer Tokens and no cookies?
Yes. Even with Bearer Tokens in headers, clickjacking can trigger unintended actions if the application embeds third-party content or lacks frame-ancestors protections. Browsers send Authorization headers with embedded requests when credentials are included, so protective headers and safe framing practices remain essential.
Is it sufficient to rely on CSRF tokens when Bearer Tokens are used for authentication?
CSRF protection is still recommended for session-based flows, but for pure Bearer Token usage (e.g., API-only clients), ensure endpoints reject unsafe methods like GET for state changes, validate origins where possible, and avoid storing tokens in cookies unless SameSite and Secure flags are set. Defense in depth with X-Frame-Options and CSP is advised.