Security¶
zipwire fetches data from remote servers and parses binary ZIP structures. When processing archives from untrusted sources, keep the following risks in mind.
Zip bombs¶
A zip bomb is a malicious archive containing entries that decompress to a vastly larger size than their compressed representation, potentially exhausting memory or disk space.
Both read() and
read_into() (and their async equivalents)
accept a max_file_size keyword argument that limits the uncompressed
size of a single entry:
from zipwire import SyncRemoteZip
from zipwire.backends import Urllib3Reader
reader = Urllib3Reader("https://example.com/untrusted.zip")
with SyncRemoteZip(reader) as rz:
for info in rz.infolist():
data = rz.read(info, max_file_size=50 * 1024 * 1024) # 50 MiB
When the limit is exceeded, FileTooLarge is raised.
Two layers of protection:
Pre-check – before decompression starts, the entry’s declared
file_sizefrom the central directory is compared against the limit. This catches honest zip bombs with no overhead.Streaming enforcement (
read_into()only) – the streaming decompressor tracks the actual decompressed output and aborts if it exceeds the limit. This catches crafted archives that lie about their uncompressed size in the metadata.
read() performs only the pre-check (layer 1). Because it
decompresses the entire entry into memory in one step, it cannot enforce
the limit during decompression. Use read_into() when processing
untrusted archives for defence in depth.
You can also inspect entries before extracting them:
MAX_SIZE = 100 * 1024 * 1024 # 100 MiB
with SyncRemoteZip(reader) as rz:
for info in rz.infolist():
if info.file_size > MAX_SIZE:
print(f"Skipping {info.filename}: too large ({info.file_size} bytes)")
continue
data = rz.read(info)
Path traversal¶
ZIP archives can contain entries with absolute paths or ../
components (e.g. ../../etc/passwd). zipwire does not extract
files to disk, so this is not a direct risk. However, if you use
filenames from the archive to construct file paths, always sanitise them:
import os
with SyncRemoteZip(reader) as rz:
for info in rz.infolist():
# Reject absolute paths and path traversal
if os.path.isabs(info.filename) or ".." in info.filename.split("/"):
print(f"Skipping suspicious path: {info.filename}")
continue
dest = os.path.join(output_dir, info.filename)
# Verify the resolved path is inside output_dir
if not os.path.realpath(dest).startswith(os.path.realpath(output_dir)):
print(f"Skipping path traversal: {info.filename}")
continue
Server trust¶
zipwire sends HTTP requests to a URL you provide. The server controls what data is returned. A malicious server could return crafted responses designed to exploit ZIP parsing bugs, cause excessive memory use, or return different data for different range requests.
Only fetch archives from servers you trust.
Use HTTPS to prevent tampering in transit.
Consider setting
max_file_sizeeven for trusted servers as a safety net.