Zip Slip in Django with Cockroachdb
Zip Slip in Django with Cockroachdb — how this specific combination creates or exposes the vulnerability
Zip Slip is a path traversal vulnerability that occurs when an application constructs file paths by directly concatenating user-supplied input with a base directory. In Django, this typically surfaces during file uploads, archive extraction, or dynamic file-serving logic. When the backend uses Cockroachdb as the persistent store, the risk pattern shifts slightly because developers may store or reference file metadata (such as uploaded file paths, temporary extraction directories, or user-supplied archive names) in the database. If validation of those paths is weak, an attacker can supply crafted filenames like ../../../etc/passwd inside a ZIP archive, and the application may write or expose files outside the intended directory.
With Cockroachdb, which is often chosen for its distributed SQL capabilities and strong consistency, developers sometimes assume that database-level constraints or ORM safeguards alone prevent unsafe paths. However, Zip Slip occurs at the application layer before any database interaction: the vulnerability is triggered when Django code processes uploaded archives, resolves paths, and writes files to disk. If the code stores only metadata in Cockroachdb (e.g., a File model with a file_path field), the unsafe path may be persisted, indexed, and later used by other services or administrative tooling, expanding the blast radius. The database becomes a persistence mechanism for malicious paths rather than the origin of the vulnerability, which means defenses must focus on input validation, path normalization, and strict sandboxing of extraction routines regardless of the database backend.
In practice, an attacker might upload a malicious ZIP to a Django endpoint that uses Cockroachdb to record upload metadata. The endpoint could extract the archive using Python’s zipfile module without verifying each member’s final resolved path. Because the path resolution logic is in Django, not in Cockroachdb, the database schema or queries do not inherently protect against traversal. The presence of Cockroachdb can also encourage storing broader file metadata or linking extracted artifacts to user records, which increases the impact if an attacker manages to write files to sensitive locations accessible by the application or administrative scripts. Therefore, the combination does not create Zip Slip by itself but can amplify impact when unsafe file handling intersects with database-backed workflows.
Cockroachdb-Specific Remediation in Django — concrete code fixes
Remediation centers on secure path handling in Django regardless of the database, with additional discipline when persisting file metadata in Cockroachdb. Always resolve and sanitize paths before any filesystem operation and avoid storing or using user-supplied paths directly. Use os.path.realpath and strict prefix checks to ensure resolved paths remain within the intended directory.
Secure file extraction with path validation
import os
import zipfile
from django.core.files.storage import default_storage
def safe_extract_zip(zip_file_path, extract_to):
extract_to = os.path.realpath(extract_to)
base_dir = os.path.abspath(extract_to)
with zipfile.ZipFile(zip_file_path, 'r') as zf:
for member in zf.infolist():
member_path = os.path.realpath(os.path.join(base_dir, member.filename))
if not member_path.startswith(base_dir + os.sep):
raise ValueError('Suspicious path in ZIP: ' + member.filename)
# Ensure parent directories exist and are within base
target_dir = os.path.dirname(member_path)
if not target_dir.startswith(base_dir):
raise ValueError('Invalid target directory')
zf.extract(member, target_dir)
return True
Django model and view example with Cockroachdb metadata storage
Define a model that stores extracted metadata, ensuring paths are normalized and validated before persistence.
from django.db import models
import os
from django.core.files.storage import default_storage
def user_directory_path(instance, filename):
# instance is the UploadFile model; use instance.id only after save
ext = os.path.splitext(filename)[1]
return f'user_{instance.user_id}/uploads/{instance.safe_name}{ext}'
class UploadFile(models.Model):
user = models.ForeignKey('auth.User', on_delete=models.CASCADE)
original_name = models.CharField(max_length=255)
safe_name = models.CharField(max_length=255) # validated/sanitized name
file_path = models.CharField(max_length=512) # relative path within storage
uploaded_at = models.DateTimeField(auto_now_add=True)
def save(self, *args, **kwargs):
# Ensure safe_name is set with validated filename before super().save()
super().save(*args, **kwargs)
In your view, validate the ZIP, extract safely, and store only safe metadata in Cockroachdb.
from django.http import JsonResponse
from django.views import View
import tempfile
import shutil
class UploadZipView(View):
def post(self, request):
uploaded = request.FILES.get('zip')
if not uploaded:
return JsonResponse({'error': 'No file'}, status=400)
with tempfile.NamedTemporaryFile(delete=False) as tmp:
for chunk in uploaded.chunks():
tmp.write(chunk)
tmp_path = tmp.name
try:
with tempfile.TemporaryDirectory() as td:
safe_extract_zip(tmp_path, td)
# After safe extraction, persist metadata to Cockroachdb
upload_obj = UploadFile.objects.create(
user=request.user,
original_name=uploaded.name,
safe_name='processed_' + os.path.basename(uploaded.name),
file_path=os.path.relpath(td, '/safe/base')
)
return JsonResponse({'status': 'ok', 'id': upload_obj.id})
except Exception as e:
return JsonResponse({'error': str(e)}, status=400)
finally:
os.unlink(tmp_path)
Additional safeguards: configure Django’s FILE_UPLOAD_PERMISSIONS, avoid dynamic path concatenation with user input, and use allowlists for file extensions and MIME types. When using Cockroachdb, store only normalized paths or identifiers, never raw user-supplied paths, and enforce uniqueness and ownership checks on retrieval to prevent confused-depot issues.
Frequently Asked Questions
Can Zip Slip happen if I store files in Cockroachdb as BLOBs?
Does using Django’s FileSystemStorage mitigate Zip Slip with Cockroachdb?
get_valid_name, but you must still avoid passing user input directly into path operations. Combine it with explicit path checks (e.g., ensuring os.path.realpath starts with your base directory) and do not rely on storage utilities alone to prevent traversal.