Skip to content

Insecure SECRET_KEY default and unsafe image upload handling #147

@kallal79

Description

@kallal79

Summary

The application relied on a weak default SECRET_KEY and accepted file uploads without sufficient server-side validation. Together these create a high-risk path that can allow session tampering and unsafe or malformed files to be stored and processed.

Root cause

  • SECRET_KEY defaulted to a development placeholder ('dev') when not provided via environment or config. In production this allows forging or tampering with signed cookies and other signed data.
  • The upload endpoint saved files using the original filename and did not verify the file content beyond a sanitized filename. This allowed non-image or malicious content to be persisted, risking downstream processing issues or exposure.

Files changed

  • dreamsApp/app/config.py — add SECRET_KEY, MAX_CONTENT_LENGTH, and ALLOWED_EXTENSIONS.
  • dreamsApp/app/__init__.py — require a secure SECRET_KEY in non-development environments and ensure MAX_CONTENT_LENGTH default.
  • dreamsApp/app/ingestion/routes.py — enforce allowed extensions, unique filenames, and verify uploaded files are valid images with Pillow (PIL.Image.verify).

Key code changes (representative excerpts)

  1. config.py (added configuration variables):
# Security: SECRET_KEY should be provided via environment in production.
SECRET_KEY = os.getenv("SECRET_KEY", "dev")

# Max upload size (default 16 MiB)
MAX_CONTENT_LENGTH = int(os.getenv("MAX_CONTENT_LENGTH", 16 * 1024 * 1024))

# Allowed image extensions for uploads
ALLOWED_EXTENSIONS = set(x.strip().lower() for x in os.getenv("ALLOWED_EXTENSIONS", "png,jpg,jpeg,gif").split(','))
  1. __init__.py (production check):
flask_env = os.getenv('FLASK_ENV', app.config.get('FLASK_ENV'))
secret = app.config.get('SECRET_KEY')
if flask_env != 'development' and (not secret or secret == 'dev'):
    raise RuntimeError('SECRET_KEY must be set to a secure value in production')
  1. ingestion/routes.py (upload validation):
# Validate extension against allowed set
allowed = current_app.config.get('ALLOWED_EXTENSIONS', {'png', 'jpg', 'jpeg', 'gif'})
if '.' not in filename or filename.rsplit('.', 1)[1].lower() not in allowed:
    return jsonify({'error': 'Unsupported file extension'}), 400

# Make filename unique
unique_filename = f"{uuid.uuid4().hex}_{filename}"
image_path = os.path.join(upload_path, unique_filename)
image.save(image_path)

# Verify the saved file is a valid image
try:
    with Image.open(image_path) as img:
        img.verify()
except (UnidentifiedImageError, Exception):
    os.remove(image_path)
    return jsonify({'error': 'Uploaded file is not a valid image'}), 400
  • OSError: [Errno 28] No space left on device while pip downloading/writing wheels — dependency installation failed.
  • Because installation was incomplete, tests aborted with missing modules: ModuleNotFoundError: No module named 'flask', No module named 'bson', No module named 'networkx'.
  • A binary compatibility error was also observed during partial import of compiled extensions: ValueError: numpy.dtype size changed, may indicate binary incompatibility. Expected 96 from C header, got 88 from PyObject — indicates mixed/incompatible binary wheels or mismatched binary packages for the Python interpreter.

Excerpt of key error lines (developer-safe):

  • ERROR: Could not install packages due to an OSError: [Errno 28] No space left on device
  • ModuleNotFoundError: No module named 'flask'
  • ValueError: numpy.dtype size changed, may indicate binary incompatibility.

Implication: the code changes are applied; CI/local validation is blocked by environment problems (disk space + binary dependency mismatch). The security risk remains urgent for any production environment that uses the previous defaults.

Test the upload endpoint manually (POST /upload) with a non-image file renamed to .jpg. The endpoint should reject it with 400 Uploaded file is not a valid image.

Validation checklist (what to verify before closing)

  • Set SECRET_KEY in production environment (or secrets manager) and verify app no longer raises at startup.
  • Confirm MAX_CONTENT_LENGTH is enforced for large uploads (server rejects large requests).
  • Confirm upload endpoint rejects unsupported extensions and non-image content disguised with image extensions.
  • Run full test suite (CI) with a clean environment; resolve binary compatibility issues if they appear.
  • Review webserver configuration to ensure uploaded files directory is not directly served or is access-restricted.
  • Add CI checks for insecure defaults (e.g., fail on SECRET_KEY='dev' in production modes) and add a dependency-scan step.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions