Improper limitation of a pathname to a restricted directory ('Path Traversal')

  • Rule ID: go_gosec_filesystem_ziparchive
  • Languages: go
  • Source: ziparchive.yml

Description

The application is at risk of a path traversal vulnerability, commonly known as 'Zip Slip', when extracting files from untrusted archives. Maliciously crafted archive files can contain relative path specifications that lead to writing files outside the intended directory when extracted, potentially overwriting system files or placing unauthorized files in critical paths.

Remediations

To safeguard against 'Zip Slip' and related exploitation techniques, follow these security practices:

✅ Limit Archive Size

Implement checks to ensure the zip archive's size does not exceed a maximum threshold, preventing 'Zip Bombs'—archives that decompress to disproportionately large sizes.

✅ Generate Unique Filenames

Avoid using the original filenames from the archive. If necessary, use only the base name after sanitizing or, better yet, generate a unique name to prevent intentional overwrites.

✅ Validate Extraction Paths

Confirm that extracted files are written to a specified, trusted directory and do not traverse outside of this directory.

✅ Disallow Symbolic Links

Only process regular files. Exclude symbolic links to prevent indirect file read/write vulnerabilities.

import (
"archive/zip"
"io"
"log"
"os"
"path/filepath"
"strings"
)

func main() {
// Open the zip file for reading
r, err := zip.OpenReader("trusted.zip")
if err != nil {
log.Fatal(err)
}
defer r.Close()

// Set up restrictions and base path
const (
expectedFileCount = 10
totalAllowedSize = 10 * 1024 * 1024 // 10MB
maxFileSize = 1024 * 1024 // 1MB
basePath = "/var/restricted/"
)

// Calculate total size of uncompressed files
var totalSize uint64
for _, f := range r.File {
totalSize += f.UncompressedSize64
}

// Check if total size exceeds the limit
if totalSize > totalAllowedSize {
log.Fatalf("archive exceeds total allowed size: %d\n", totalSize)
}

// Process files in the archive
for _, f := range r.File {
// Skip overlarge files
if f.UncompressedSize64 > maxFileSize {
log.Printf("skipping file as it exceeds maxFileSize: %s\n", f.Name)
continue
}

// Skip if not a regular file
if !f.Mode().IsRegular() {
log.Printf("skipping non-regular file: %s\n", f.Name)
continue
}

// Securely resolve the file path
name := filepath.Base(f.Name)
resolvedPath, err := filepath.Join(basePath, name)
if err != nil {
log.Fatal(err)
}

// Ensure the file does not traverse outside the base path
if !strings.HasPrefix(resolvedPath, basePath) {
log.Fatal("path does not start with basePath")
}

// Extract and process the file (omitted for brevity)
}
}

For processing directories within the zip archive, ensure to clean the path and validate it strictly against the base path.

Resources

Associated CWE

OWASP Top 10

Configuration

To skip this rule during a scan, use the following flag

bearer scan /path/to/your-project/ --skip-rule=go_gosec_filesystem_ziparchive

To run only this rule during a scan, use the following flag

bearer scan /path/to/your-project/ --only-rule=go_gosec_filesystem_ziparchive

Ready to take the next step? Learn more about Bearer Cloud.