Zip Slip in Buffalo with Api Keys
Zip Slip in Buffalo with Api Keys — how this specific combination creates or exposes the vulnerability
Zip Slip is a path traversal vulnerability where an attacker-controlled archive causes files to be extracted outside the intended directory. When an API endpoint in Buffalo uses Api Keys for identification but does not properly validate or sanitize file paths during archive extraction, the combination can expose or overwrite arbitrary files on the server. Even though Api Keys identify the caller, Buffalo may still process the extracted paths relative to a base directory without canonicalization, allowing sequences like ../../etc/passwd to escape the intended location.
Consider a Buffalo API that accepts an uploaded zip and uses a provided Api Key to associate the operation with a user. If the server uses the filename from the archive directly—without checking for path traversal sequences—and writes to a directory scoped by the Api Key (e.g., user-specific storage), an attacker can supply a crafted zip containing files with paths such as ../../../secrets/api_keys.txt. Upon extraction, files land outside the user’s directory, potentially exposing sensitive data or enabling overwrite of configuration files. Because the scan tests unauthenticated attack surfaces, middleBrick will flag Insecure Deserialization or Path Traversal findings tied to archive handling, even when Api Key checks exist only at the endpoint entry point.
The risk is compounded when endpoints return information about extracted files or when symbolic links are not resolved. For example, a Buffalo handler that trusts the archive’s metadata may inadvertently disclose paths or contents linked to the Api Key context. middleBrick’s checks for Data Exposure and Input Validation will highlight whether path normalization occurs and whether file operations remain confined to the intended scope, regardless of Api Key presence.
Api Keys-Specific Remediation in Buffalo — concrete code fixes
To mitigate Zip Slip when Api Keys are used in Buffalo, ensure path sanitization and strict scope enforcement around archive extraction. Always resolve and validate file paths against a canonical base directory, and avoid deriving filesystem paths from user-supplied archive entries. Below are concrete remediation patterns with real, syntactically correct Buffalo Go code examples.
1. Validate and clean archive paths
Use filepath.Clean and filepath.Rel to ensure extracted paths remain within the intended directory. Do not trust archive metadata alone.
import (
"archive/zip"
"io/fs"
"net/http"
"path/filepath"
)
func extractZipSafe(baseDir, zipPath string) error {
r, err := zip.OpenReader(zipPath)
if err != nil {
return err
}
defer r.Close()
for _, f := range r.File {
// Clean the filename and ensure it remains inside baseDir
cleanName := filepath.Clean(f.Name)
if cleanName == ".." || len(cleanName) > 0 && cleanName[0] == '.' {
return ErrPathTraversal
}
// Ensure the path resolves within baseDir
target := filepath.Join(baseDir, cleanName)
if target, err = filepath.Rel(baseDir, target); err != nil || target == ".." {
return ErrPathTraversal
}
if err := f.extract(baseDir); err != nil {
return err
}
}
return nil
}
2. Scope extraction by Api Key context
Bind extraction to a per-Api Key directory and avoid concatenating user input directly into paths. Treat the Api Key as an authorization scope, not a filesystem prefix.
func handler(w http.ResponseWriter, r *http.Request) {
apiKey := r.Header.Get("X-API-Key")
if apiKey == "" {
http.Error(w, "missing api key", http.StatusUnauthorized)
return
}
// Map Api Key to a scoped base directory (ensure this mapping is server-side)
baseDir := filepath.Join("/srv/uploads", apiKey)
if err := os.MkdirAll(baseDir, 0700); err != nil {
http.Error(w, "server error", http.StatusInternalServerError)
return
}
file, header, err := r.FormFile("archive")
if err != nil {
http.Error(w, "invalid form", http.StatusBadRequest)
return
}
defer file.Close()
tmpPath := filepath.Join(baseDir, header.Filename)
if err := os.WriteFile(tmpPath, nil, 0600); err != nil {
http.Error(w, "server error", http.StatusInternalServerError)
return
}
if err := os.WriteFile(tmpPath, readAllBytes(file)); err != nil {
http.Error(w, "server error", http.StatusInternalServerError)
return
}
if err := extractZipSafe(baseDir, tmpPath); err != nil {
http.Error(w, "extraction failed", http.StatusBadRequest)
return
}
w.WriteHeader(http.StatusOK)
}
3. Prefer fs.FS and restrict file modes
When possible, use io/fs subsystems to enforce in-memory checks and avoid direct filesystem writes from archive entries.
func copyToFSSafe(archive io.ReaderAt, size int64, destFS fs.FS) error {
zipReader, err := zip.NewReader(archive, size)
if err != nil {
return err
}
for _, f := range zipReader.File {
cleanName := filepath.Clean(f.Name)
if cleanName == ".." || strings.HasPrefix(cleanName, "..") {
return ErrPathTraversal
}
rc, err := f.Open()
if err != nil {
return err
}
defer rc.Close()
// Write to destFS using cleanName, ensuring no traversal escapes destFS
// Example: use io.Copy with a file opened via destFS.OpenFile
}
return nil
}
By combining path canonicalization, scope-bound directories per Api Key, and strict validation, Buffalo services reduce Zip Slip exposure while still leveraging Api Keys for identification. middleBrick will note improvements in Input Validation and Data Exposure findings when these controls are in place.