CSP-D503: Insecure Extraction of zipfile Archives (Zip Slip)¶
Vulnerability Category: Filesystem
Severity: HIGH
Description¶
This rule flags the insecure extraction of .zip archives using the zipfile module. This vulnerability is identical in principle to the one found in tarfile (CSP-D502) and is also known as "Zip Slip".
A malicious .zip file can contain filenames with path traversal sequences (e.g., ../.._app/main.py). If an application extracts this archive without validating the filenames, it can allow an attacker to overwrite arbitrary files on the system where the application has write permissions. This can lead to remote code execution, denial of service, or other malicious outcomes.
This rule specifically targets zipfile.ZipFile.extract() and zipfile.ZipFile.extractall() when used on untrusted archives.
Vulnerable Code Example¶
import zipfile
# Assume 'malicious.zip' is an untrusted archive.
# It contains a file with the name "../../../etc/hosts" which
# could be used to reroute network traffic on the server.
with zipfile.ZipFile("malicious.zip", "r") as zip_ref:
# This call is vulnerable. It will extract the malicious file
# outside of the intended 'output_dir'.
zip_ref.extractall("output_dir")
Safe Code Example¶
Unlike tarfile in recent Python versions, zipfile does not have a built-in filter to prevent this attack. The only way to safely extract a zip file is to manually iterate over each file in the archive and validate its path before extraction.
import zipfile
import os
destination_dir = os.path.abspath("output_dir")
with zipfile.ZipFile("archive.zip", "r") as zip_ref:
for member in zip_ref.infolist():
# Build the full path for the member
member_path = os.path.join(destination_dir, member.filename)
# Resolve the absolute path
real_member_path = os.path.abspath(member_path)
# Check if the resolved path is within the destination directory
if not real_member_path.startswith(destination_dir):
print(f"Illegal path in zip archive: {member.filename}")
continue
# The path is safe, so extract it
zip_ref.extract(member, path=destination_dir)
destination_dir. How to Suppress a Finding¶
You should only suppress this finding if you are extracting an archive from a fully trusted source where you are certain that it contains no malicious paths.
import zipfile
# This archive is generated by a trusted internal process.
# ignore
with zipfile.ZipFile("trusted_archive.zip", "r") as zip_ref:
zip_ref.extractall("output_dir")
Or, for this specific rule: