HIGH zip slipflask

Zip Slip in Flask

Flask-Specific Remediation

Securing Flask against Zip Slip requires proper archive validation and safe extraction practices. The key is validating both the archive structure and the extraction target before writing any files.

Here's a secure Flask implementation using multiple defense layers:

from flask import Flask, request, jsonify
import zipfile
import os
from pathlib import Path

app = Flask(__name__)
UPLOAD_DIR = Path('/var/www/uploads')
MAX_ARCHIVE_SIZE = 10 * 1024 * 1024  # 10MB

# Allowlist of safe file extensions
SAFE_EXTENSIONS = {'.txt', '.pdf', '.jpg', '.png', '.csv', '.json'}

def is_safe_path(base_dir: Path, target_path: Path) -> bool:
    """Validate that target_path is within base_dir"""
    try:
        # Resolve both paths and check if target is within base
        return str(target_path.resolve()).startswith(str(base_dir.resolve()))
    except (OSError, RuntimeError):
        return False

def validate_archive_entry(entry: zipfile.ZipInfo) -> bool:
    """Validate a single archive entry"""
    # Check for path traversal
    if '..' in entry.filename or entry.filename.startswith('/'):
        return False
    
    # Check for Windows path traversal
    if '\' in entry.filename:
        return False
    
    # Check file extension against allowlist
    ext = os.path.splitext(entry.filename)[1].lower()
    if ext not in SAFE_EXTENSIONS:
        return False
    
    return True

@app.route('/secure-upload', methods=['POST'])
def secure_upload():
    if 'file' not in request.files:
        return jsonify(error='No file provided'), 400
    
    file = request.files['file']
    if file.content_length > MAX_ARCHIVE_SIZE:
        return jsonify(error='File too large'), 413
    
    if not file.filename.endswith('.zip'):
        return jsonify(error='Only ZIP files allowed'), 400
    
    # Create a temporary file for the upload
    temp_path = UPLOAD_DIR / 'temp_upload.zip'
    file.save(temp_path)
    
    try:
        with zipfile.ZipFile(temp_path, 'r') as zip_ref:
            # First pass: validate all entries
            for entry in zip_ref.infolist():
                if not validate_archive_entry(entry):
                    return jsonify(error=f'Unsafe file in archive: {entry.filename}'), 400
                
                # Check extraction path
                target_path = UPLOAD_DIR / entry.filename
                if not is_safe_path(UPLOAD_DIR, target_path):
                    return jsonify(error=f'Path traversal detected: {entry.filename}'), 400
            
            # Second pass: safe extraction
            for entry in zip_ref.infolist():
                target_path = UPLOAD_DIR / entry.filename
                target_path.parent.mkdir(parents=True, exist_ok=True)
                with zip_ref.open(entry) as source, target_path.open('wb') as dest:
                    dest.write(source.read())
                    
    except zipfile.BadZipFile:
        return jsonify(error='Invalid ZIP file'), 400
    finally:
        # Clean up temporary file
        if temp_path.exists():
            temp_path.unlink()
    
    return jsonify(success=True, message='File uploaded securely'), 200

Key security improvements in this Flask-specific remediation:

  1. Path validation: is_safe_path() ensures extracted files stay within the intended directory using Path.resolve()
  2. Entry validation: validate_archive_entry() checks for traversal sequences and allows only safe file types
  3. Two-pass extraction: First validates all entries, then extracts—prevents partial extraction of malicious files
  4. Safe file handling: Uses context managers and proper binary file operations
  5. Size limits: Prevents large archive attacks with MAX_ARCHIVE_SIZE

For production Flask applications, consider using the zipfile.ZipFile.extract() method with explicit path validation instead of extractall(), and implement rate limiting on upload endpoints to prevent abuse.

Frequently Asked Questions

How does Zip Slip differ from other directory traversal attacks in Flask?
Zip Slip specifically exploits archive extraction where malicious paths are embedded within ZIP files. Unlike simple directory traversal via URL parameters, Zip Slip hides traversal sequences inside archives, making them harder to detect. In Flask, this is particularly dangerous because extracted Python files can be automatically reloaded, and template files in templates/ directories are automatically discovered.
Can middleBrick detect Zip Slip vulnerabilities in my Flask application?
Yes, middleBrick specifically tests for Zip Slip by uploading crafted ZIP archives containing path traversal sequences and verifying if files are written outside the intended directory. The scanner runs 12 security checks including Input Validation and Authentication bypass testing, and provides detailed findings with severity ratings and remediation guidance specific to your Flask application's implementation.