Zip Slip in Fastapi with Dynamodb
Zip Slip in Fastapi with Dynamodb — 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 a Fastapi service that interacts with Amazon DynamoDB, the risk is not in DynamoDB itself but in how uploaded files or dynamic paths are handled before data is stored or retrieved from DynamoDB. For example, if a Fastapi endpoint accepts a filename or key and uses it to build a local filesystem path without validation, an attacker can provide a specially crafted filename like ../../../etc/passwd. When the application later uses this path to read or write a file that is associated with a DynamoDB record (such as a reference to a stored object or metadata), the traversal can escape the intended directory. Because DynamoDB stores metadata or pointers rather than the file content, the vulnerability surfaces when the application resolves local paths based on untrusted input before performing DynamoDB operations.
Consider a Fastapi endpoint that stores an uploaded file and saves its path in a DynamoDB table. If the endpoint uses user input to construct the file path without normalization or strict validation, the resulting path may traverse outside the intended storage directory. Later, when another endpoint reads the file using the path stored in DynamoDB, the malicious path can lead to unauthorized file access or overwriting of critical system files. DynamoDB entries that reference these unsafe paths become conduits for traversal, especially when combined with insufficient path sanitization. This pattern is common in applications that use DynamoDB as a catalog for assets while relying on local filesystem storage, where the integrity of the path depends entirely on application-level checks.
The interaction between Fastapi routing, dynamic path generation, and DynamoDB record handling amplifies the impact of Zip Slip. An attacker may exploit weak validation to access or modify files that should be isolated, potentially exposing sensitive data referenced by DynamoDB items. Since DynamoDB does not enforce filesystem semantics, the responsibility for safe path construction falls entirely on the application. Therefore, any endpoint that accepts file or directory names and later uses them in filesystem operations—especially when those operations are tied to DynamoDB records—must enforce strict allowlists, canonicalization, and directory confinement to prevent traversal.
Dynamodb-Specific Remediation in Fastapi — concrete code fixes
To remediate Zip Slip in a Fastapi application that uses DynamoDB, ensure that all user-controlled path components are validated, sanitized, and resolved against a strict base directory before any filesystem interaction. Below is a secure Fastapi endpoint example that stores a file and records metadata in DynamoDB using the AWS SDK for Python (Boto3).
import os
from pathlib import Path
from fastapi import Fastapi, UploadFile, File, HTTPException
import boto3
from botocore.exceptions import ClientError
app = Fastapi()
dynamodb = boto3.resource('dynamodb', region_name='us-east-1')
table_name = os.getenv('DYNAMODB_TABLE', 'secure-uploads')
table = dynamodb.Table(table_name)
BASE_DIR = Path('/safe/upload/dir').resolve()
@app.post('/upload/')
async def upload_file(file: UploadFile = File(...)):
# Validate filename: allow only alphanumeric, underscores, hyphens, and a single extension
filename = file.filename
if not filename:
raise HTTPException(status_code=400, detail='Filename is required')
# Use Path to resolve and prevent traversal
safe_name = Path(filename).name # discard any directory components
target = BASE_DIR / safe_name
# Ensure the resolved path stays within BASE_DIR
try:
target.resolve().relative_to(BASE_DIR.resolve())
except ValueError:
raise HTTPException(status_code=400, detail='Invalid path')
# Save the file securely
with target.open('wb') as buffer:
buffer.write(await file.read())
# Record metadata in DynamoDB
try:
table.put_item(Item={
'file_id': str(uuid4()),
'original_name': filename,
'stored_path': str(target),
'size': target.stat().st_size
})
except ClientError as e:
# Clean up the file if DynamoDB write fails
if target.exists():
target.unlink()
raise HTTPException(status_code=500, detail='Failed to record metadata')
return {'message': 'Upload successful', 'file_id': 'xxx'}
Key remediation practices include using Path.name to strip directory segments, resolving paths with .resolve(), and enforcing a relative_to check against the canonical base directory. Avoid using string concatenation or os.path.join with untrusted input. For DynamoDB, store only the safe resolved path or an identifier, and never rely on user input to construct filesystem locations. Apply the same validation to any endpoint that retrieves files based on DynamoDB records, ensuring that read paths are reconstructed from trusted identifiers rather than stored raw values.