How to securely upload files
Store files safely using Magic Byte inspection, file size guards, and path traversal defenses.
File uploads are high-risk entry points. ZCore's LocalStorageProvider defends your server against Path Traversal, Denial of Service (large files), and executable masquerading (e.g., PHP scripts or .exe binaries disguised as .jpg).
1. Configure the Storage Provider
Initialize LocalStorageProvider with your security validators and register it in the IoC container:
# main.py or storage_config.py
from zcore.storage import (
LocalStorageProvider,
SafeMimeTypeValidator,
MaxFileSizeValidator,
FileExtensionValidator,
StorageProvider
)
from zcore.kernel.di import container
storage = LocalStorageProvider(
base_path="./storage/uploads",
url_prefix="/storage",
validators=[
# 1. Enforce strict 5 MB file size limit
MaxFileSizeValidator(max_size_mb=5),
# 2. Check file extension
FileExtensionValidator(allowed_extensions=[".png", ".jpg", ".jpeg", ".webp", ".pdf"]),
# 3. Inspect binary Magic Bytes and scan against disguised scripts/XSS
SafeMimeTypeValidator(allowed_mimes=["image/png", "image/jpeg", "image/webp", "application/pdf"])
]
)
# Register as the active storage provider singleton
container.register_singleton(StorageProvider, storage)2. Create the Upload Endpoint
Inject StorageProvider into your FastAPI route and upload the UploadFile payload:
# routers.py
from fastapi import APIRouter, UploadFile, File
from zcore import Inject
from zcore.storage import StorageProvider
router = APIRouter(prefix="/media", tags=["Media"])
@router.post("/avatar")
async def upload_avatar(
file: UploadFile = File(...),
storage: Inject[StorageProvider] = None
):
# Saves to ./storage/uploads/avatars/ with collision-free UUID filename
# Returns normalized web URL: "/storage/avatars/a3b8...png"
web_url = await storage.upload(file, folder="avatars")
return {"url": web_url}Web URL Resolution: Both upload() and upload_stream() save the file securely and immediately return the normalized, web-accessible public path (e.g., /storage/avatars/5f8c41...png) ready to be persisted into your database or sent back to clients.
3. Secure File Deletion
Delete stored assets safely. ZCore accepts either the web URL or relative path, verifies that the target resides strictly inside base_path, and prevents sandbox traversal:
@router.delete("/avatar")
async def delete_avatar(
url: str,
storage: Inject[StorageProvider]
):
# Accepts "/storage/avatars/5f8c41...png" or relative paths
success = await storage.delete(url)
return {"deleted": success}Triple-Layer Security Architecture:
- Magic Bytes & Script Inspection: Reads the initial 8192 header bytes against known binary signatures and scans against PHP execution tags (
<?php), HTML scripts (<script), event handlers (onerror=), Unix shebangs (#!/), and Windows PE headers (MZ). - Path Traversal Defense:
upload(),upload_stream(), anddelete()resolve system paths strictly and assert.is_relative_to(base). Any directory breakout attempt (e.g.,../../etc/passwd) is safely blocked. - Collision-Free Naming: Uploaded files are automatically renamed with high-entropy randomized UUIDs (
uuid4().hex + ext) to prevent filesystem overwrites and preserve privacy.