Symlink Attack in Fiber with Api Keys
Symlink Attack in Fiber with Api Keys — how this specific combination creates or exposes the vulnerability
A symlink attack in a Fiber-based API that relies on API keys occurs when an endpoint uses user-controlled path information to locate files or resources and resolves those paths on the server filesystem. If the application constructs file paths by concatenating or joining user input with a base directory, an attacker can provide a specially crafted value that traverses directories or replaces intended files with a symbolic link (symlink). Because the endpoint validates access using API keys rather than filesystem permissions, the attack leverages the trust placed in the key to bypass ownership or location checks.
Consider a scenario where a Fiber handler serves user documents by taking a document ID, looking up a path from a database, and then reading the file from disk. If an attacker can register or manipulate a document entry so that its stored path points to a location outside the intended directory, and then use a valid API key to call the endpoint, the server may follow symlinks introduced by the attacker. This can lead to unauthorized file reads (information disclosure) or, in configurations that allow deletion or overwrite, unintended modification of system or application files. The API key ensures the request is authenticated, but it does not restrict what the server process can do with the resolved path, enabling the symlink to redirect file operations.
In the context of the 12 security checks run by middleBrick, this pattern maps to BOLA/IDOR and Input Validation. The scanner tests whether authenticated (via key) endpoints safely resolve user-influenced paths and whether directory traversal or symlink-prone inputs are reflected in runtime behavior. Even though the scan is unauthenticated in the sense that it does not require credentials to be configured, it can probe endpoints that accept API keys and attempt path-based injection techniques to detect unsafe resolution logic.
Real-world attack patterns such as CVE-2021-41773-style path traversal and symlink-assisted reads illustrate the risk. If an API uses keys but does not canonicalize paths or ensure that resolved locations remain within a chroot-like boundary, an attacker with a valid key can read files not intended for that key scope. This becomes especially relevant when combined with insecure default configurations or when the API serves files uploaded by users without strict content-type and name validation.
Api Keys-Specific Remediation in Fiber — concrete code fixes
To mitigate symlink risks in Fiber endpoints that use API keys, ensure that file paths are derived safely and that user input never directly controls filesystem locations. Use a strict allowlist for identifiers that map to files, resolve paths with filepath.Clean and filepath.Rel, and confirm that the resulting path remains within the intended base directory. Avoid using user input to build shell commands or to construct symlinks, and do not rely on API keys alone for filesystem isolation.
Below is a secure Fiber handler example that uses API keys for authentication and safely resolves file paths without exposing symlink or traversal risks:
package main
import (
"fmt"
"io/fs"
"net/http"
"os"
"path/filepath"
"github.com/gofiber/fiber/v2"
)
const baseDir = "/srv/api/files"
func safeFileHandler(c *fiber.Ctx) error {
apiKey := c.Get("Authorization")
if apiKey == "" {
return c.Status(http.StatusUnauthorized).SendString("missing key")
}
// Example: validate key against a secure store in production
if apiKey != "Bearer valid_example_key" {
return c.Status(http.StatusForbidden).SendString("invalid key")
}
docID := c.Params("docid")
// Ensure docID is alphanumeric and non-empty to prevent path injection
if docID == "" || containsDotDot(docID) {
return c.Status(http.StatusBadRequest).SendString("invalid document ID")
}
// Build a clean, absolute path and ensure it stays within baseDir
requested := filepath.Join(baseDir, docID)
cleanRequested := filepath.Clean(requested)
rel, err := filepath.Rel(baseDir, cleanRequested)
if err != nil || startsWithDotDot(rel) {
return c.Status(http.StatusForbidden).SendString("forbidden path")
}
// Confirm resolved entry is a regular file and not a symlink to an unexpected location
info, err := os.Lstat(cleanRequested)
if err != nil {
return c.Status(http.StatusNotFound).SendString("not found")
}
if info.Mode()&os.ModeSymlink != 0 {
// Reject symlinks to avoid redirection
return c.Status(http.StatusForbidden).SendString("symlink not allowed")
}
if info.IsDir() {
return c.Status(http.StatusBadRequest).SendString("not a file")
}
data, err := os.ReadFile(cleanRequested)
if err != nil {
return c.Status(http.StatusInternalServerError).SendString("read error")
}
return c.Send(data)
}
// containsDotDot returns true if s contains ".." path element.
func containsDotDot(s string) bool {
return s == ".." || len(s) >= 2 && (s[:2] == ".." || s[len(s)-2:] == ".." || containsPathSep(s))
}
func startsWithDotDot(s string) bool {
return len(s) >= 2 && s[0] == '.' && s[1] == '.'
}
func containsPathSep(s string) bool {
for i := 0; i < len(s); i++ {
if s[i] == '/' || s[i] == '\\' {
return true
}
}
return false
}
func main() {
app := fiber.New()
app.Get("/files/:docid", safeFileHandler)
app.Listen(":3000")
}
This example demonstrates key practices: validating and sanitizing input, using filepath.Join and filepath.Clean to normalize paths, checking that the resolved path remains within the intended base directory, rejecting symlinks, and returning appropriate HTTP status codes. In production, API key validation should integrate with a secure key store and rate limiting should be applied to reduce abuse risk.