Zip Slip in Flask with Bearer Tokens
Zip Slip in Flask with Bearer Tokens — how this specific combination creates or exposes the vulnerability
Zip Slip is a path traversal vulnerability that occurs when an application constructs file paths using user-supplied input without proper validation. In a Flask application that uses Bearer Tokens for authentication, the combination of file extraction logic and token handling can inadvertently expose sensitive endpoints or enable unauthorized access if path traversal is possible during archive extraction.
Consider a Flask route that accepts an uploaded ZIP file and extracts it to a destination directory. If the archive contains entries with crafted paths such as ../../../etc/passwd, and the server uses simple string concatenation to build the extraction path, files can be written outside the intended directory. When Bearer Tokens are used, an authenticated client includes the token in the Authorization header. If the server also uses the token value (or a portion of it) to derive a storage location or to name extracted artifacts without sanitization, an attacker can manipulate the archive contents to traverse directories and potentially overwrite or expose token-related files or configuration artifacts.
For example, an API endpoint that receives a ZIP and a Bearer token might extract the archive into a per-token directory like /data/uploads/{token}/. If the ZIP includes a file named ../../config/secrets.yml, and the server does not sanitize the member path, the file could escape the token directory and write into system configuration paths. This exposes secrets that may be referenced by the token-handling logic. Even if the token itself is not stored in the file system, an attacker can probe whether token-scoped directories are created and whether traversal can reach other user directories, enabling enumeration or privilege escalation across tenant boundaries (a BOLA/IDOR pattern).
The risk is compounded when the server returns information about extraction results or logs paths without redaction. An attacker can include paths designed to traverse into directories where Bearer Token artifacts (such as cached credentials or temporary keys) might exist, and then infer the internal layout via error messages or directory listing responses. Because middleBrick tests for Path Traversal and BOLA/IDOR in parallel, scans of such an endpoint would flag both the traversal risk and the potential for unauthorized access across user boundaries.
In summary, Zip Slip in Flask becomes critical in the presence of Bearer Tokens when token-derived paths or token-related data are used to determine where extracted content is written. Without strict input validation and path canonicalization, an attacker can leverage a malicious archive to escape intended storage boundaries and access or corrupt data associated with the token scope.
Bearer Tokens-Specific Remediation in Flask — concrete code fixes
To mitigate Zip Slip in Flask when using Bearer Tokens, ensure that file paths are validated and isolated per token scope. Below are concrete, secure code examples demonstrating proper handling.
Secure extraction with path sanitization
Use os.path.normpath and os.path.commonpath to ensure extracted members remain within the intended directory. Do not rely on the archive member path alone.
import os
import zipfile
from flask import Flask, request, abort
app = Flask(__name__)
def safe_mkdir(token_dir):
os.makedirs(token_dir, exist_ok=True)
def extract_safe(zip_data, token):
token_dir = os.path.abspath(f'/data/uploads/{token}')
safe_mkdir(token_dir)
with zipfile.ZipFile(zip_data) as zf:
for member in zf.namelist():
# Remove leading slash and dots to prevent directory traversal
member = member.lstrip('/')
member = '/'.join([p for p in member.split('/') if p and p != '..'])
member_path = os.path.normpath(member)
# Ensure final path stays within token_dir
full_path = os.path.join(token_dir, member_path)
if not os.path.commonpath([token_dir, full_path]) == token_dir:
abort(400, 'Invalid path in archive')
zf.extract(member, full_path)
return True
@app.route('/upload', methods=['POST'])
def upload():
auth = request.headers.get('Authorization', '')
if not auth.startswith('Bearer '):
abort(401, 'Missing Bearer token')
token = auth[7:].strip()
if not token:
abort(400, 'Invalid token')
file = request.files.get('archive')
if not file:
abort(400, 'Missing archive')
# Pass file stream and token to secure extractor
if extract_safe(file.stream, token):
return {'status': 'ok'}, 200
Token isolation and input validation
Always treat the Bearer Token as an identifier, not as part of the filesystem path without sanitization. Use a deterministic mapping to storage directories and avoid including raw token characters in filenames.
import hashlib
import uuid
def token_storage_dir(token):
# Derive a safe directory name from the token hash
token_hash = hashlib.sha256(token.encode()).hexdigest()
return os.path.abspath(f'/data/uploads/{token_hash}')
def store_upload(file_stream, token):
base = token_storage_dir(token)
safe_mkdir(base)
filename = str(uuid.uuid4()) + '.bin'
filepath = os.path.join(base, filename)
file_stream.save(filepath)
return filepath
Dependency and configuration hygiene
Ensure that any third‑party extraction libraries are pinned to versions without known Zip Slip vulnerabilities. Avoid recursive extraction and disable symlink creation during extraction.
# Example with zipfile (standard library) — safe defaults
with zipfile.ZipFile(archive_path) as zf:
for info in zf.infolist():
if hasattr(info, 'is_dir') and info.is_dir():
continue
# Do not extract if would escape target
member_names = [info.filename]
for name in member_names:
if name.endswith('/'):
continue
# Validate as above
By combining strict path canonicalization, token hashing for storage directories, and rejecting archives with unsafe members, Flask applications can safely handle user uploads while preserving token isolation and avoiding Zip Slip.