Zip Slip in Gin with Api Keys
Zip Slip in Gin with Api Keys — how this specific combination creates or exposes the vulnerability
Zip Slip is a path traversal vulnerability where an attacker-controlled archive leads to files being extracted outside the intended directory. When an API built with the Gin framework uses static file serving and also relies on API keys for access control, the combination can expose sensitive files if path validation is weak and authentication is misunderstood as authorization.
In Gin, a typical pattern is to use Static or StaticFile to serve assets, often behind a handler that checks for an API key in a header. If the API key gate is applied only to the route entry point and not enforced per-file, and if user input directly influences the file path without proper sanitization, an authenticated request (with a valid API key) can traverse directories. For example, a request like /download?file=../../../etc/passwd may pass the API key check and then allow reading files outside the public folder. This means the presence of API keys does not automatically prevent path traversal; it can create a false sense of security if validation is limited to authentication alone.
The vulnerability is realized when the server concatenates user input to a base directory without canonicalizing the path and ensuring it remains within the intended prefix. An attacker with a valid API key can exploit this to read arbitrary files (e.g., source code, configuration, or secrets) or, in more severe cases involving writable endpoints, write malicious content. Note that API keys in Gin are typically validated in middleware, but if the middleware does not reject requests with invalid or missing keys for all downstream handlers, or if path traversal is possible in static file handling, the attack surface remains.
middleBrick scans such endpoints (including those using API keys) and tests the unauthenticated attack surface; if your API exposes file-serving routes without strict path validation, a scan can identify related findings like Path Traversal. Remember, middleBrick detects and reports — it does not fix or block — so you must apply secure coding practices to remediate.
Api Keys-Specific Remediation in Gin — concrete code fixes
To fix Zip Slip in Gin when API keys are used, ensure strict path validation and keep authentication separate from authorization logic. Use filepath.Clean and verify that the resolved path remains within the allowed directory. Do not rely on API key middleware alone to prevent path traversal.
Example of an insecure handler that concatenates user input and is vulnerable to Zip Slip:
package main
import (
"github.com/gin-gonic/gin"
"net/http"
"path/filepath"
"strings"
)
func main() {
r := gin.Default()
r.GET("/download", func(c *gin.Context) {
requestedFile := c.Query("file")
// Insecure: no path cleaning or base restriction
fullPath := filepath.Join("/var/www/public", requestedFile)
c.File(fullPath) // Zip Slip possible
})
r.Run()
}
Remediation steps:
- Canonicalize and restrict the file path using
filepath.Cleanand ensure it does not escape the base directory. - Validate the file extension and type if applicable.
- Use
http.DirwithOpenandReaddirfor more control, or usehttp.ServeFilewith a computed safe path. - Keep API key validation in middleware but ensure it applies to all routes; do not assume a key permits access to all filesystem paths.
Secure example with path validation:
package main
import (
"github.com/gin-gonic/gin"
"net/http"
"os"
"path/filepath"
)
func safeFilePath(base, requested string) (string, error) {
clean := filepath.Clean(requested)
if strings.Contains(clean, ".."+string(os.PathSeparator)) {
return "", os.ErrInvalid
}
full := filepath.Join(base, clean)
if rel, err := filepath.Rel(base, full); err != nil || rel == ".." || strings.HasPrefix(rel, ".."+string(os.PathSeparator)) {
return "", os.ErrInvalid
}
return full, nil
}
func main() {
r := gin.Default()
r.GET("/download", func(c *gin.Context) {
requestedFile := c.Query("file")
const baseDir = "/var/www/public"
filePath, err := safeFilePath(baseDir, requestedFile)
if err != nil {
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "invalid file path"})
return
}
// Ensure file exists and is within baseDir
if _, err := os.Stat(filePath); os.IsNotExist(err) {
c.AbortWithStatus(http.StatusNotFound)
return
}
c.File(filePath)
})
r.Run()
}
Additionally, if you use API keys via middleware, ensure it rejects requests before they reach the file handler:
package main
import (
"github.com/gin-gonic/gin"
"net/http"
)
func apiKeyMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
token := c.GetHeader("X-API-Key")
if token != "YOUR_SECRET_KEY" {
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "invalid api key"})
return
}
c.Next()
}
}
func main() {
r := gin.Default()
r.Use(apiKeyMiddleware())
r.GET("/public", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{"message": "public endpoint"})
})
r.Run()
}
By combining secure path handling with consistent API key validation, you reduce the risk of Zip Slip and ensure that authenticated access does not inadvertently expose filesystem traversal.