Skip to content

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)
This code ensures that no file can be written outside of the intended 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:

# ignore: CSP-D503
with zipfile.ZipFile("trusted_archive.zip", "r") as zip_ref:
    zip_ref.extractall("output_dir")