Path Traversal in Gorilla Mux with Hmac Signatures
Path Traversal in Gorilla Mux with Hmac Signatures — how this specific combination creates or exposes the vulnerability
Path Traversal occurs when user-controlled path segments are concatenated directly into file system operations, allowing an attacker to resolve paths outside the intended directory (e.g., via ../../../etc/passwd). In Gorilla Mux, routes often capture variables such as {filename} or {filepath} and pass them to handlers that open or serve files. If input validation and canonicalization are missing, an attacker can traverse directories regardless of how the request is authenticated or authorized.
Combining Gorilla Mux with Hmac Signatures introduces a nuanced risk: developers may assume that cryptographic integrity of request metadata (e.g., query parameters or headers represented as an HMAC) guarantees safety of path variables. For example, an API endpoint might verify an HMAC to ensure the request has not been tampered with, then use a path variable directly in a filesystem call. The presence of Hmac Signature validation can create a false sense of security, leading to relaxed checks on path inputs. In black-box scanning, middleBrick tests unauthenticated attack surfaces and can detect whether Hmac-protected endpoints still suffer from Path Traversal by probing path traversal sequences in URL path segments or parameters.
Consider a handler where the route includes a versioned path and a filename, and an HMAC is computed over selected query parameters to prevent tampering:
// Example: Hmac verification applied to query params, but path variable used unsafely
func downloadHandler(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
filename := vars["filename"] // user-controlled
// Verify Hmac over query parameters
given := r.URL.Query().Get("hmac")
computed := computeHmac(r.URL.Query().Get("fileId") + r.URL.Query().Get("ts"))
if !hmac.Equal([]byte(given), []byte(computed)) {
http.Error(w, "invalid signature", http.StatusUnauthorized)
return
}
// Unsafe: filename used directly
path := filepath.Join("/srv/uploads", filename)
http.ServeFile(w, r, path)
}
Even though the Hmac protects the integrity of fileId and ts, the filename variable remains untrusted. An attacker can supply ../../../etc/passwd as filename and, if the server does not sanitize or restrict the path, read arbitrary files. The Hmac check does not mitigate Path Traversal because it operates on different inputs and does not constrain the filesystem namespace. middleBrick’s checks for Data Exposure and Input Validation will flag such endpoints, noting that authentication or integrity mechanisms on one parameter do not substitute for proper path canonicalization and allow-listing.
Another scenario involves query parameters used to build paths indirectly. For instance, an endpoint might construct storage paths from a user-supplied prefix while using Hmac to bind a session token:
// Hmac binds session token, but path prefix is concatenated unsafely
func serveDocument(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
prefix := vars["prefix"]
// Verify Hmac over session token and document ID
token := r.Header.Get("X-Session-Token")
documentID := r.URL.Query().Get("doc_id")
if !isValidHmac(token, documentID) {
http.Error(w, "invalid hmac", http.StatusForbidden)
return
}
// Unsafe path construction
fullPath := filepath.Join("/data", prefix, documentID)
http.ServeFile(w, r, fullPath)
}
Here, prefix can include traversal sequences, and the Hmac over token and documentID does not prevent directory escape. The scanner will highlight this as a finding under Path Traversal and Input Validation, emphasizing that Hmac Signature verification is not a replacement for input sanitization, path normalization, or restricting file access to a controlled directory tree.
Hmac Signatures-Specific Remediation in Gorilla Mux — concrete code fixes
To remediate Path Traversal when using Hmac Signatures in Gorilla Mux, treat path variables as untrusted regardless of cryptographic protections on other parameters. Apply strict allow-listing, canonicalize paths, and avoid direct filesystem joins with user input. Below are concrete, safe patterns.
1) Use filepath.Clean and restrict to a base directory with no traversal escape
Always call filepath.Clean and verify that the cleaned path remains within the intended base directory. Do not rely on Hmac checks on unrelated parameters to enforce path safety.
func downloadHandler(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) filename := vars["filename"] // Verify Hmac over relevant parameters (unchanged) given := r.URL.Query().Get("hmac") computed := computeHmac(r.URL.Query().Get("fileId") + r.URL.Query().Get("ts")) if !hmac.Equal([]byte(given), []byte(computed)) { http.Error(w, "invalid signature", http.StatusUnauthorized) return } // Safe path resolution base := "/srv/uploads" cleaned := filepath.Clean("/" + filename) // leading slash to make it relative path := filepath.Join(base, filepath.Clean(cleaned)) if !strings.HasPrefix(path, filepath.Clean(base)+string(os.PathSeparator)) && path != filepath.Clean(base) { http.Error(w, "path traversal attempt", http.StatusBadRequest) return } http.ServeFile(w, r, path) }2) Use a strict allow-list for filenames or file extensions
Instead of trying to sanitize arbitrary names, permit only known-safe patterns. This is especially effective when combined with Hmac-protected metadata, ensuring that business integrity checks and filesystem safety are independent and explicit.
var allowedExts = map[string]bool{".pdf": true, ".txt": true, ".jpg": true} func serveDocument(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) prefix := vars["prefix"] documentID := r.URL.Query().Get("doc_id") // Hmac verification on session and documentID if !isValidHmac(r.Header.Get("X-Session-Token"), documentID) { http.Error(w, "invalid hmac", http.StatusForbidden) return } // Validate prefix against allow-list if prefix != "public" && prefix != "internal" { http.Error(w, "invalid prefix", http.StatusBadRequest) return } // Safe: documentID should be UUID or integer; join with cleaned prefix base := filepath.Join("/data", prefix) cleanedID := filepath.Clean("/" + documentID) fullPath := filepath.Join(base, cleanedID) if !strings.HasPrefix(fullPath, filepath.Clean(base)+string(os.PathSeparator)) && fullPath != filepath.Clean(base) { http.Error(w, "invalid path", http.StatusBadRequest) return } http.ServeFile(w, r, fullPath) }3) Use http.Dir and Open-file policies to enforce namespace boundaries
Leverage
http.Dirsemantics withOpento ensure that any attempt to escape results in an error before serving content.func safeServeFile(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) filename := vars["filename"] // Hmac checks omitted for brevity base := http.Dir("/srv/uploads") // Open uses Clean internally; if the result is outside base, it returns error file, err := base.Open(filename) if err != nil { http.Error(w, "file not found", http.StatusNotFound) return } defer file.Close() stat, _ := file.Stat() if stat.IsDir() { http.Error(w, "directories not served", http.StatusBadRequest) return } http.ServeContent(w, r, stat.Name(), stat.ModTime(), file) }In all cases, Hmac Signature verification can remain in place to ensure integrity of business parameters, but it must be complemented by explicit path controls. middleBrick’s checks for BOLA/IDOR and Input Validation will confirm that path variables are properly constrained and that no bypass is possible through encoded or malformed sequences.
Related CWEs: inputValidation
| CWE ID | Name | Severity |
|---|---|---|
| CWE-20 | Improper Input Validation | HIGH |
| CWE-22 | Path Traversal | HIGH |
| CWE-74 | Injection | CRITICAL |
| CWE-77 | Command Injection | CRITICAL |
| CWE-78 | OS Command Injection | CRITICAL |
| CWE-79 | Cross-site Scripting (XSS) | HIGH |
| CWE-89 | SQL Injection | CRITICAL |
| CWE-90 | LDAP Injection | HIGH |
| CWE-91 | XML Injection | HIGH |
| CWE-94 | Code Injection | CRITICAL |