HIGH zip slipdjangocockroachdb

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?
Storing files as BLOBs in Cockroachdb does not prevent Zip Slip during extraction. The vulnerability arises when you write extracted files to the filesystem using unsanitized names. Even if the file content resides in the database, any temporary extraction or serving logic must validate and sanitize paths to prevent traversal.
Does using Django’s FileSystemStorage mitigate Zip Slip with Cockroachdb?
Django’s FileSystemStorage helps by letting you define a storage location and using 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.