Direct use of an uploaded file name

Direct use of an uploaded file name

Description

Using an uploaded file's original name directly as a storage path or object name can allow path traversal such as ../, unintended overwrites, or files with executable extensions. Do not trust the original name; generate or sanitize the name on the server.

Potential impact

  • Files outside the upload directory may be overwritten.
  • Existing files or configuration may be damaged.
  • Malicious files may be saved under a path served by the application.

Remediation

  • Do not use client-supplied file names directly as storage paths or keys.
  • With Flask/Werkzeug, sanitize names with secure_filename() or generate a server-side name such as a UUID.
  • With Django/FastAPI, also generate a UUID, random token, or name defined by server policy instead of using the uploaded name.
  • Restrict destination directories and file extensions to an allow-list.

Examples

Before

python
import os
from flask import request

def bad_upload():
    file = request.files["file"]
    file.save(os.path.join("uploads", file.filename))

After

Prepare UPLOAD_DIR as a private directory writable only by the application.

python
import uuid
from pathlib import Path
from flask import abort, request
from werkzeug.utils import secure_filename

UPLOAD_DIR = Path("/srv/app/upload-quarantine")
ALLOWED_EXTENSIONS = {".png", ".jpg", ".jpeg"}

def safe_upload():
    file = request.files["file"]
    original_name = secure_filename(file.filename or "")
    extension = Path(original_name).suffix.lower()
    if extension not in ALLOWED_EXTENSIONS:
        abort(400, description="Unsupported file extension")

    stored_name = f"{uuid.uuid4().hex}{extension}"
    file.save(UPLOAD_DIR / stored_name)
    return {"id": stored_name}, 201

Explanation:

  • Before: The original file name is joined directly to the destination path.
  • After: The sanitized original name is used only to check the extension policy. Only permitted extensions are stored in the private upload directory. The actual name is a server-generated UUID, so client-controlled paths, absolute paths, and overwrite targets do not become the file-system path. If file format is a security boundary, verify the MIME type and actual content separately.

References