HIGH broken authenticationdjangobearer tokens

Broken Authentication in Django with Bearer Tokens

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

Django does not include a built-in Bearer token implementation for API authentication; developers typically add token handling using packages such as djangorestframework-simplejwt or custom middleware. When Bearer tokens are used incorrectly, the authentication boundary that should protect endpoints breaks down, leading to Broken Authentication. Several patterns commonly weaken the security of token-based flows in Django.

One vulnerability pattern is weak or predictable token generation. If tokens are derived from low-entropy values (e.g., sequential user IDs or timestamps), attackers can guess valid tokens and assume identities of other users without possessing a legitimate token. In Django, this often occurs when custom token generators omit cryptographic randomness or rely solely on user attributes such as username or email.

A second pattern is insufficient transport protection. Bearer tokens must be transmitted exclusively over HTTPS. Without enforced TLS, tokens can be intercepted in transit via passive sniffing or active man-in-the-middle attacks. In development, forgetting to set settings.DEBUG = False and omitting HTTP Strict Transport Security (HSTS) can inadvertently allow tokens to be sent over HTTP, exposing them to interception.

Third, improper token storage on the client side leads to exposure. Bearer tokens stored in local storage are accessible to JavaScript and vulnerable to cross-site scripting (XSS). If a Django application serves API responses to JavaScript clients without appropriate Content Security Policy (CSP) and relies on tokens in local storage, a reflected or stored XSS flaw can result in token theft and unauthorized API access.

Misconfigured token validation is another contributor. In Django, token verification should include checks such as token expiration, audience, and issuer claims (for JWTs). Skipping these checks or accepting unsigned tokens (e.g., algorithm=['none'] in some JWT libraries) allows attackers to forge tokens. Similarly, using a weak signing algorithm like HS256 with a shared secret that is hardcoded or exposed in client-side code weakens integrity.

Additionally, missing or weak rate limiting on token validation endpoints enables brute-force and credential-stuffing attacks against token introspection or login endpoints. Without per-client or per-IP rate limits, an attacker can systematically test stolen or guessed tokens to discover valid ones. In Django REST Framework, omitting throttle classes on views that verify tokens leaves these endpoints open to automated abuse.

Finally, token revocation and logout handling are often incomplete. Bearer tokens are typically stateless; if a compromised token remains valid until natural expiration and there is no mechanism to revoke it (e.g., a denylist or short-lived tokens with refresh rotation), an attacker can continue using a stolen token. In Django, failing to implement token blacklisting or short expirations increases the window of exposure for Broken Authentication.

Bearer Tokens-Specific Remediation in Django — concrete code fixes

Remediation focuses on strengthening token generation, transport, storage, validation, rate limiting, and revocation. Below are concrete practices and code examples for Django projects using Bearer tokens.

1. Use cryptographically strong token generation

Ensure tokens are generated with sufficient entropy. For custom token models, use secrets.token_urlsafe instead of predictable values.

import secrets
from django.db import models

class ApiToken(models.Model):
    user = models.ForeignKey('auth.User', on_delete=models.CASCADE)
    token = models.CharField(max_length=255, unique=True)
    created_at = models.DateTimeField(auto_now_add=True)

    def save(self, *args, **kwargs):
        if not self.token:
            self.token = secrets.token_urlsafe(32)  # 256 bits of entropy
        super().save(*args, **kwargs)

2. Enforce HTTPS in production

Require HTTPS for all API traffic and set HSTS headers. In settings, configure secure transport for production environments.

# settings.py
if not DEBUG:
    SECURE_SSL_REDIRECT = True
    SECURE_HSTS_SECONDS = 31536000
    SECURE_HSTS_INCLUDE_SUBDOMAINS = True
    SECURE_HSTS_PRELOAD = True
    SESSION_COOKIE_SECURE = True
    CSRF_COOKIE_SECURE = True

3. Store tokens securely on the client

Serve API clients with strong CSP and avoid storing Bearer tokens in local storage. For SPAs, prefer short-lived tokens in memory and use secure, HTTP-only cookies for refresh tokens when possible.

4. Validate token claims rigorously

If using JWTs, validate expiration (exp), audience (aud), and issuer (iss). Do not accept unsigned tokens.

# Example using PyJWT with strict validation
import jwt
from django.conf import settings

def decode_token(encoded_token):
    try:
        payload = jwt.decode(
            encoded_token,
            settings.SECRET_KEY,
            algorithms=['HS256'],
            audience='myapi',
            issuer='django-auth',
        )
        return payload
    except jwt.ExpiredSignatureError:
        raise ValueError('Token expired')
    except jwt.InvalidTokenError:
        raise ValueError('Invalid token')

5. Apply rate limiting to token-related endpoints

Use Django REST Framework throttling to protect token validation and login endpoints.

# throttles.py
from rest_framework.throttling import AnonRateThrottle

class TokenAnonRateThrottle(AnonRateThrottle):
    rate = '100/day;10/hour'

# views.py
from rest_framework.throttling import UserRateThrottle
from .throttles import TokenAnonRateThrottle

class TokenVerifyView(APIView):
    throttle_classes = [TokenAnonRateThrottle, UserRateThrottle]

    def post(self, request):
        token = request.data.get('token')
        # validate token logic
        return Response({'valid': True})

6. Implement token revocation and short lifetimes

Use short expiration times for access tokens and maintain a denylist for revoked tokens. Rotate refresh tokens and bind tokens to client context where feasible.

# models.py
class RevokedToken(models.Model):
    token_id = models.CharField(max_length=255, unique=True)
    revoked_at = models.DateTimeField(auto_now_add=True)

# utils.py
def is_token_revoked(token_id):
    return RevokedToken.objects.filter(token_id=token_id).exists()

# In token verification flow, check revocation
if is_token_revoked(payload.get('jti')):
    raise ValueError('Token revoked')

Related CWEs: authentication

CWE IDNameSeverity
CWE-287Improper Authentication CRITICAL
CWE-306Missing Authentication for Critical Function CRITICAL
CWE-307Brute Force HIGH
CWE-308Single-Factor Authentication MEDIUM
CWE-309Use of Password System for Primary Authentication MEDIUM
CWE-347Improper Verification of Cryptographic Signature HIGH
CWE-384Session Fixation HIGH
CWE-521Weak Password Requirements MEDIUM
CWE-613Insufficient Session Expiration MEDIUM
CWE-640Weak Password Recovery HIGH

Frequently Asked Questions

Does middleBrick test for Broken Authentication in API scans?
Yes, middleBrick runs an Authentication check among its 12 parallel security checks and reports whether authentication mechanisms, including token handling, are correctly enforced.
Can the Django Bearer token examples be adapted for other frameworks?
Yes, the principles—strong token generation, HTTPS enforcement, strict token validation, rate limiting, and revocation—are applicable across frameworks; implementation details will differ.