DEV Community

StarkMan
StarkMan

Posted on

Archive extraction needs a resource and path policy

Archive extraction needs a resource and path policy

Uploading an archive and extracting it later is a common application feature. It can also turn a small untrusted file into writes outside the destination, unexpected symbolic links, enormous disk use, or a denial of service. The extraction library is only one part of the control. The application needs a policy for paths, link types, size, file count, processing time, and the identity that handles the result.

Path traversal is the best-known failure: an archive member contains ../ segments and the extractor joins it to a destination without checking the resolved location. Symbolic links create a different problem. A link inside the archive can point to a sensitive path and a later member can write through it. Absolute paths, hard links, device nodes, and repeated names add more cases. A file name check that only removes .. substrings is not a sufficient design.

Validate before extraction, then validate again

Inspect the member list before writing anything. Reject absolute paths, parent traversal, duplicate names where they create ambiguity, and unsupported link or device types. Normalize each member path and confirm that it remains under the intended root. Check both the lexical path and the resolved filesystem path, because an existing symlink can change the meaning of a later join.

Extraction should happen under a fresh directory that the application controls, ideally in a disposable worker with a writable quota and no access to secrets or broad host mounts. Do not extract directly into the application's code directory or a shared upload directory. Keep ownership and mode bits under application policy rather than trusting archive metadata.

Python's tarfile documentation provides extraction filters that limit dangerous functionality. Current versions can use a data filter, and the documentation explicitly notes that even a filter is not a complete answer for live, untrusted data. Check the runtime version and set the policy explicitly when the default is not sufficient. Equivalent checks are needed for ZIP and other formats.

Bound the work, not only the bytes

A compressed file can expand far beyond its upload size. Set a limit on compressed bytes, uncompressed bytes, member count, individual file size, total extraction time, and the number of nested archive layers you will process. Count bytes as they are written rather than trusting the declared size in the archive header. Stop the worker when a limit is exceeded and remove the partial output according to a defined cleanup policy.

The CPU and memory used to validate names and decompress data are part of the attack surface. A scanning process that follows links or opens files to determine their type can itself become a gadget. Run the parser with reduced privileges, avoid unbounded recursion, and keep the processing queue isolated from the web process.

Make the result explicit

After extraction, enumerate the resulting files again. Confirm that the real paths remain under the worker root and that the application did not create links to places it cannot safely serve. Store a manifest of names, sizes, hashes, and content types. If the application converts or scans the files, use a new output directory rather than modifying the uploaded package in place.

Test the following cases in a non-production worker:

  • a normal archive with nested directories
  • an absolute member name
  • parent traversal in the stored name
  • a symbolic link to a file outside the destination
  • a hard link with an unexpected target
  • two members with names that normalize to the same path
  • a highly compressed file that exceeds the output limit
  • a corrupt archive that fails partway through extraction

The useful control is not a single regex. It is a small, isolated extraction service with explicit path validation, hard resource limits, a defensible file policy, and observable failure. Once those pieces are in place, an archive upload becomes a managed input instead of a request to run the server's filesystem operations.

References

[1] Python tarfile extraction filters

[2] OWASP File Upload Cheat Sheet

[3] OWASP testing guide, malicious file uploads

Top comments (0)