HIGH replay attackdjangobearer tokens

Replay Attack in Django with Bearer Tokens

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

A replay attack occurs when an attacker intercepts a valid request and retransmits it to reproduce the original effect. In Django, using Bearer tokens (typically passed via the Authorization header) without additional protections makes APIs susceptible to this class of issue. Because Bearer tokens are static credentials for the duration of their validity, an intercepted token can be reused by an attacker to impersonate the original caller, even if the underlying transport uses TLS.

When an API endpoint accepts a Bearer token and performs only token validation (e.g., checking signature and scope) without ensuring request uniqueness, replay becomes possible. For example, consider a payment or state-changing endpoint that relies solely on the token for authentication but does not include a nonce, timestamp, or idempotency key. An attacker who captures the Authorization header and the full request can replay the call within the token’s lifetime to perform unauthorized actions, such as initiating duplicate transactions or escalating permissions.

The risk is compounded if the token is long-lived or if the API does not enforce strict transport-layer requirements. Even with HTTPS, a compromised token or a TLS-terminating proxy that logs headers can expose credentials. Moreover, some middleware or load balancers might inadvertently log Authorization headers, increasing the exposure surface. Developers might mistakenly assume that HTTPS alone prevents replay, but without additional mechanisms like one-time nonces or strict timestamp windows, replay remains feasible.

In Django, this often manifests in views or viewsets that check token validity but do not track request identifiers or enforce one-time use. The API may also lack rate limiting at the account or token level, allowing an attacker to submit the same crafted request multiple times. Because middleBrick tests authentication and BOLA/IDOR checks in parallel, it can detect endpoints where token-based authentication is present but replay protections such as unique request identifiers or idempotency are missing.

An example of vulnerable Django REST Framework code that accepts Bearer tokens but does not guard against replay:

from rest_framework.views import APIView
from rest_framework.request import Request
from rest_framework.response import Response
from rest_framework.permissions import IsAuthenticated

class PaymentView(APIView):
    permission_classes = [IsAuthenticated]

    def post(self, request):
        # Vulnerable: no nonce or idempotency key; same token & payload can be replayed
        amount = request.data.get('amount')
        # Process payment logic here
        return Response({'status': 'processed'})

In this snippet, if an attacker captures the Authorization header (e.g., Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...) and the request body, they can replay the POST to trigger duplicate payments. middleBrick’s checks for Authentication and Property Authorization highlight endpoints where tokens are accepted without additional context to ensure request uniqueness.

Bearer Tokens-Specific Remediation in Django — concrete code fixes

To mitigate replay attacks when using Bearer tokens in Django, implement request-level uniqueness controls such as nonces or idempotency keys, and enforce short token lifetimes where feasible. Combine these with server-side tracking of used identifiers and strict timestamp validation to reject replays.

Use Idempotency Keys: Require clients to send a unique idempotency key (e.g., a UUID) in a header, and store processed keys with a TTL longer than the maximum acceptable replay window. This ensures that repeated identical requests are ignored or return the original response instead of re-executing side effects.

import uuid
from django.utils import timezone
from django.core.cache import cache
from rest_framework.views import APIView
from rest_framework.request import Request
from rest_framework.response import Response
from rest_framework.permissions import IsAuthenticated

class IdempotentPaymentView(APIView):
    permission_classes = [IsAuthenticated]

    def post(self, request):
        idempotency_key = request.META.get('HTTP_X_IDEMPOTENCY_KEY')
        if not idempotency_key:
            return Response({'error': 'X-Idempotency-Key header required'}, status=400)

        # Use cache to store key with a TTL (e_response example: 24 hours)
        cache_key = f'idempotency:{idempotency_key}'
        if cache.get(cache_key):
            return Response({'status': 'duplicate request, already processed'})

        # Process payment
        amount = request.data.get('amount')
        # ... actual processing ...

        cache.set(cache_key, {'status': 'processed'}, timeout=86400)
        return Response({'status': 'processed', 'idempotency_key': idempotency_key})

Validate Timestamps and Reject Stale Requests: Include a timestamp in the request (e.g., an X-Request-Timestamp header) and reject requests where the timestamp falls outside an acceptable window (for example, ±5 minutes). This limits the window during which a captured request can be reused.

from django.utils import timezone
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.permissions import IsAuthenticated
import time

class TimestampedPaymentView(APIView):
    permission_classes = [IsAuthenticated]
    ALLOWED_CLOCK_SKEW_SECONDS = 300  # 5 minutes

    def post(self, request):
        timestamp_header = request.META.get('HTTP_X_REQUEST_TIMESTAMP')
        if not timestamp_header:
            return Response({'error': 'X-Request-Timestamp header required'}, status=400)

        try:
            request_timestamp = int(timestamp_header)
        except ValueError:
            return Response({'error': 'Invalid timestamp'}, status=400)

        current_time = int(time.time())
        if abs(current_time - request_timestamp) > self.ALLOWED_CLOCK_SKEW_SECONDS:
            return Response({'error': 'Request timestamp outside allowed window'}, status=400)

        # Proceed with processing
        amount = request.data.get('amount')
        return Response({'status': 'processed', 'received_at': request_timestamp})

Shorten Token Lifetimes and Use Refresh Flows: Configure token expiration to be as short as practical and encourage clients to use refresh tokens to obtain new access tokens. While this does not directly prevent replay within a single token’s validity, it reduces the impact window. In Django, this can be managed through your token generation and validation logic, ensuring tokens include an exp claim and are verified on each request.

# Example JWT payload with expiration
import jwt
from datetime import datetime, timedelta

payload = {
    'sub': 'user-123',
    'scope': 'payments:write',
    'iat': datetime.utcnow(),
    'exp': datetime.utcnow() + timedelta(minutes=15)  # short-lived
}
token = jwt.encode(payload, 'your-secret-key', algorithm='HS256')
# Include in Authorization: Bearer {token}

Combine these techniques—idempotency keys, timestamp windows, and short-lived tokens—to significantly reduce the feasibility of replay attacks against Django APIs using Bearer tokens. middleBrick’s scans can validate the presence of these controls by checking Authentication and Property Authorization configurations alongside runtime behavior, helping you prioritize fixes based on actual risk.

Frequently Asked Questions

Can replay attacks happen over HTTPS if tokens are intercepted?
Yes. HTTPS protects confidentiality in transit, but if an attacker captures a valid Bearer token and request, they can replay it while the token is valid. HTTPS alone does not prevent replay; you need nonces, timestamps, or idempotency keys.
How does middleBrick detect missing replay protections?
middleBrick runs parallel checks including Authentication and Property Authorization. It identifies endpoints that accept Bearer tokens and flags those lacking unique request identifiers, idempotency mechanisms, or timestamp validation, providing prioritized findings and remediation guidance.