Zip Slip in Django with Dynamodb
Zip Slip in Django with Dynamodb — how this specific combination creates or exposes the vulnerability
Zip Slip is a path traversal vulnerability that occurs when user-supplied filenames are used to construct extraction paths without proper validation. In a Django application that stores or serves files from Amazon DynamoDB, the risk emerges at the intersection of dynamic content handling and external data storage. If your Django app saves user-uploaded files to a temporary directory before or after persisting metadata to DynamoDB, and later uses filenames from DynamoDB records to extract archives or build filesystem paths, an attacker can supply crafted archive entries like ../../../etc/passwd that traverse outside the intended directory.
Consider a workflow where a user uploads a ZIP file, metadata (including the intended storage key) is written to DynamoDB, and later the app reads that key to extract or serve contents. If extraction logic uses the key or original filename directly from the DynamoDB record without canonicalizing and validating paths, an attacker can manipulate the filename to escape the target extraction folder. This is especially dangerous when the ZIP is generated or processed in a serverless or ephemeral environment, because the boundary between storage and extraction is less strict. The DynamoDB record itself may appear benign, but if it reflects attacker-controlled input from the ZIP’s internal file names, the persistence layer becomes a vector for path manipulation.
In Django, this often manifests in views that handle file downloads or archive imports, where a developer queries DynamoDB for a file record and then uses a field such as original_name or a user-provided path to build filesystem operations. Because DynamoDB does not validate filesystem semantics, it will store and return whatever string you provide. Without strict validation, the application can be tricked into writing to parent directories, overwriting sensitive files, or exposing directory traversal endpoints. The OWASP API Top 10 category ‘Broken Access Control’ maps closely to this scenario, and findings from a middleBrick scan can highlight unchecked path usage across endpoints that interact with external storage.
Dynamodb-Specific Remediation in Django — concrete code fixes
To mitigate Zip Slip in a Django application using DynamoDB, enforce strict path normalization and whitelist-based validation before any filesystem operation. Always resolve paths with os.path.realpath or Path.resolve() and ensure the resulting path remains within an allowed base directory. Avoid using raw user input from DynamoDB records to construct filesystem paths; instead, map keys to safe, server-managed filenames and maintain an allowlist of acceptable characters and patterns.
Below are concrete code examples for safe handling when integrating DynamoDB with Django file workflows.
import os
from pathlib import Path
import boto3
from django.conf import settings
# Safe helper: ensure a target path stays within a base directory
def safe_join(base, target):
base_path = Path(base).resolve()
target_path = (base_path / target).resolve()
if not str(target_path).startswith(str(base_path) + os.sep):
raise ValueError('Path traversal detected')
return target_path
# Example: writing extracted files safely after reading metadata from DynamoDB
def process_file_record(record):
# Assume record contains 's3_key' or similar, but we derive a safe local filename
safe_filename = record.get('safe_filename') # pre-validated server-side name
upload_base = settings.MEDIA_ROOT
target = safe_join(upload_base, safe_filename)
# Write file content securely (content obtained from a trusted source)
target.write_bytes(record['file_content'])
return target
# Example: querying DynamoDB and avoiding direct use of user-controlled names
client = boto3.client('dynamodb', region_name='us-east-1')
response = client.get_item(
TableName='FileMetadata',
Key={'file_id': {'S': '12345'}}
)
item = response.get('Item')
if item:
# Use server-controlled mapping; never use raw user name from item
safe_name = item.get('safe_name', {}).get('S')
if safe_name:
path_obj = safe_join(settings.EXTRACT_DIR, safe_name)
# Proceed with extraction or streaming
with open(path_obj, 'rb') as f:
data = f.read()
Additionally, apply defense in depth by scanning API endpoints with middleBrick to detect path traversal indicators and insecure usage of external storage fields. The CLI can be run via middlebrick scan <url> to uncover such issues in unauthenticated attack surfaces, while the GitHub Action can enforce security gates in CI/CD to prevent regressions that reintroduce traversal risks.