We're sorry but this app doesn't work properly without JavaScript enabled. Please enable it to continue.

This lesson's interactive features are locked, please to keep using them

Safe Archive Extraction

The fix for our file-path problems is to validate each entry name after resolving its destination path. We can use the relative path of the destination, starting from a trusted extraction root, to make sure it doesn't escape:

relativePath, err := filepath.Rel(root, destination)
if err != nil ||
    relativePath == "." ||
    relativePath == ".." ||
    strings.HasPrefix(relativePath, ".."+string(filepath.Separator)) {
    return errors.New("archive entry escapes the extraction directory")
}

In Go, filepath.Rel describes how to reach a destination from a given root. Since we want to accept only destinations that fall under the extraction directory, we reject paths that meet any of these conditions:

  • The path is ., so the destination is the root itself (bad).
  • The path is .., so the destination is the parent directory of the root (bad).
  • The path starts with .. plus the platform's filepath.Separator, so the destination is outside the root (bad).

Other platform-aware checks are also worth running against archive entries before extraction. A Windows-style path with backslashes cannot safely be treated as an ordinary document path. Nor can a symbolic link.

A symbolic link, or symlink, is a file that points to another filesystem path. These demand careful handling (or rejection). In Go, you can check if a file is a symlink by looking at its ModeSymlink bit.

For a primer on symlinks, see our Linux course.

Validate Before Writing

When it comes to archives, always validate all entry destinations before extracting any of them. If the fifth entry is malicious, but you only discover that after writing the first four, you'll get a partial extraction and leave the system in an inconsistent state.

Zip Bombs

Archive extraction also needs resource limits. Compressed input can expand dramatically, so production systems should cap the number of entries and total uncompressed size. If you don't, you're open to zip bomb attacks. They don't leak anything, but they can bring down your servers' ability to respond by exhausting their disk space or memory as a type of denial-of-service attack.

Assignment

Bearly Secure plans every ZIP entry before writing it, but it does not validate the planned paths. Reject the entire archive when any entry could escape or redirect extraction.

With Bearly Secure still running, run and submit the CLI tests from the project root.