Symlink Attack in Django with Bearer Tokens
Symlink Attack in Django with Bearer Tokens — how this specific combination creates or exposes the vulnerability
A symlink attack in Django involving Bearer tokens typically occurs when file uploads or user-controlled paths are handled in a way that allows an attacker to place a symbolic link that points to a sensitive location such as a token storage directory. If your Django application exposes an endpoint that accepts file uploads and stores files based on user input without proper path validation, an authenticated attacker with a Bearer token could manipulate the upload path to create a symlink on the server. When the application later writes a file (for example, a token refresh file, session cache, or configuration artifact) to what it believes is a safe directory, the write is redirected to a location the attacker can read or overwrite.
Bearer tokens are often stored in headers and passed to Django views for authentication. If a view also performs file operations based on request parameters or user-controlled data, the combination of token-based authentication and unsafe file handling increases risk: the token proves identity but does not enforce authorization on file system operations. Consider a scenario where an authenticated client calls an endpoint like /upload/, and the server uses a user-supplied filename or directory to determine where to save the uploaded content. Without canonicalizing paths and ensuring the target resides within an allowed directory, an attacker can supply a filename such as ../../../secrets/token_backup or craft a symlink during upload that points outside the intended storage area.
In practice, this can expose token material if the application writes logs, caches, or temporary files containing tokens to the redirected location, or if the attacker replaces a legitimate file that the application later reads when processing authenticated requests. Because the attack leverages the trust the application places in user input rather than the token itself, the presence of a valid Bearer token is simply the means by which the attacker reaches the vulnerable code path. The vulnerability is not in how tokens are transmitted or stored in headers, but in how the server uses those authenticated sessions to perform unsafe file system actions.
Bearer Tokens-Specific Remediation in Django — concrete code fixes
To mitigate symlink risks while using Bearer tokens in Django, focus on strict input validation, path canonicalization, and isolation of file operations from authentication logic. Always resolve paths with os.path.realpath or Path.resolve(strict=True) and ensure the final path is within an allowed base directory. Avoid using user input directly to derive filesystem paths, and prefer generating server-side filenames.
Example of unsafe file handling that can lead to symlink issues:
import os
from django.http import JsonResponse
def unsafe_upload(request):
# WARNING: This is vulnerable to path traversal and symlink attacks
filename = request.GET.get('filename', 'upload.txt')
upload_dir = '/var/app/uploads'
destination = os.path.join(upload_dir, filename)
with open(destination, 'wb') as f:
f.write(request.body)
return JsonResponse({'status': 'ok'})
Secure alternative using path validation and server-side naming:
import os import uuid from pathlib import Path from django.http import JsonResponse BASE_DIR = Path('/var/app/uploads').resolve() def secure_upload(request): # Use a server-generated filename to avoid user-controlled paths ext = '.bin' # derive or enforce a safe extension safe_name = f'{uuid.uuid4().hex}{ext}' destination = (BASE_DIR / safe_name).resolve() # Ensure the resolved path is still inside the allowed directory if not str(destination).startswith(str(BASE_DIR) + os.sep): return JsonResponse({'error': 'Invalid path'}, status=400) destination.write_bytes(request.body) return JsonResponse({'filename': safe_name})If you must accept a filename, normalize and validate it rigorously:
from pathlib import Path import os def normalize_filename(user_name: str) -> str: p = Path(user_name).resolve() # Ensure the resolved path does not escape the base directory base = Path('/var/app/uploads').resolve() try: p.relative_to(base) except ValueError: raise ValueError('Path traversal detected') return str(p)For Bearer token usage, keep token handling separate from file operations. Authenticate via token in middleware, but do not allow request-derived paths to influence where authenticated sessions write files. If you integrate middleBrick in your pipeline, you can add API security checks to CI/CD to catch such misconfigurations before deployment.