Guide

Extracting user-uploaded archives safely

Path traversal, symlink escapes, compression bombs and nested archives — what your extraction code must handle, and safe test files for the rest.

Extracting a ZIP or tarball from a user looks like a one-line library call. It is also one of the most common sources of file-system vulnerabilities. An archive’s entry names, links and sizes are all attacker-controlled. Here is what to check. SampleFiles.com only hosts safe files, so for the attack cases (1–3) we describe the defense rather than offer a malicious archive; build those fixtures inside your own test suite, where they can’t be mistaken for real downloads.

1. Path traversal (“Zip Slip”)

An entry named ../../etc/cron.d/job makes naive code write outside the target folder. This class of bug (CVE-2018-1002200 and many relatives) has affected libraries in almost every language.

Fix: resolve each entry’s destination path and confirm it is still inside the extraction directory before writing:

Python
dest = (target / entry_name).resolve()
if not dest.is_relative_to(target.resolve()):
    raise ValueError(f"Unsafe path in archive: {entry_name}")

Also reject absolute paths, and on Windows, drive letters and backslashes.

Tar archives can contain symbolic links. If you extract a link pointing to ../../../../etc/hosts and then extract a file with the same name, the write follows the link. Some extractors also follow links while reading.

Fix: skip symlinks and hard links unless you need them. If you do need them, validate the link target the same way as file paths. Python 3.12+ has tarfile.extractall(filter='data'), which does this for you.

3. Compression bombs

Compression ratios can be extreme. A few kilobytes of ZIP can expand to gigabytes and fill the disk or memory. Don’t trust the sizes declared in the archive headers, because they can lie.

Fix: count bytes as you actually write them, and stop once you pass a total limit (for example, 10× the upload size or a fixed cap). Also limit the number of entries and the nesting depth.

Safe test files for entry and depth limits: 1000-files.zip, nested-3-levels.zip

4. Names and encodings

Entry names may be non-ASCII, use a legacy code page instead of UTF-8, or collide on case-insensitive file systems (Readme.txt and README.txt). Generate your own safe names if you store the files, and keep the original names only for display.

Test file: unicode-names.zip

5. Broken archives

Truncated uploads and bit rot produce archives that list correctly but fail to extract, or can’t be opened at all. Fail with a clear error and delete any partial output.

Test files: truncated.zip, crc-mismatch.zip

6. Encrypted archives

Decide whether you support them. If you don’t, detect encryption up front and say so, instead of failing halfway through. Encrypted 7z archives can hide even the file names.

Test files: password-protected ZIP, password-protected 7Z (password: samplefiles)

See the archive extraction use case for all of these files together.

Sample files for this guide