HIGH symlink attackfiberhmac signatures

Symlink Attack in Fiber with Hmac Signatures

Symlink Attack in Fiber with Hmac Signatures — how this specific combination creates or exposes the vulnerability

A symlink attack in the Fiber web framework can occur when user-controlled paths are used to access files, and the application does not validate or sanitize these paths. If an endpoint builds file system paths from request parameters (e.g., a download route that reads files relative to a base directory), an attacker can provide a path containing .. sequences or a symbolic link (symlink) pointing outside the intended directory. This can lead to path traversal or unauthorized file access.

When Hmac Signatures are used in Fiber, they are typically applied to ensure request integrity—often to sign query parameters, route values, or a combination of method + path + body. If the signature is computed over the raw, unsanitized path (including user-controlled segments used to build file system paths), an attacker might not be able to change the signature without knowing the secret. However, if the server recomputes the signature on a normalized path after resolving symlinks—or if the signature does not cover the resolved file path used in file operations—there is a mismatch between what was signed and what is accessed. This can allow an attacker to leverage a symlink on the server to read or overwrite files outside the intended scope, while still presenting a valid signed request.

Consider a scenario where a Fiber endpoint serves files from a directory using a query parameter file, and the server uses Hmac Signatures to verify that the parameter has not been tampered with. If the server resolves the file path with filepath := filepath.Clean(baseDir + c.QueryParams("file")) and then follows any symlinks via os.Stat or os.Open, the resolved path can escape the base directory if a symlink exists within it. The Hmac verification might have passed because the original query string was valid, but the actual file accessed is different due to the symlink. This is a BOLA/IDOR-like issue where authorization checks are bypassed via path manipulation, and the signature does not protect the file resolution step.

Real-world analogs include CVE patterns where path traversal or symlink races are combined with insufficient input validation. Even with Hmac Signatures ensuring request authenticity, the server must treat file-system paths as untrusted and resolve them within a controlled, canonical location before accessing the file. Failing to do so exposes the application to unauthorized data exposure or manipulation despite cryptographic integrity checks.

Hmac Signatures-Specific Remediation in Fiber — concrete code fixes

To remediate symlink risks while using Hmac Signatures in Fiber, ensure that file paths are resolved and constrained to a safe directory before any file operation, and that the Hmac covers the canonical path used for access. Do not rely on the signature to prevent path traversal; enforce strict path validation separately.

Use filepath.Clean and filepath.Join to construct paths, and then verify that the cleaned path remains within the intended base directory using filepath.Rel or by checking the prefix. Avoid passing user input directly to file system functions, and do not rely on symlink resolution for authorization decisions.

Example secure Fiber handler with Hmac verification and safe path resolution:

package main

import (
	"crypto/hmac"
	"crypto/sha256"
	"fmt"
	"io"
	"net/http"
	"path/filepath"
	"strings"

	"github.com/gofiber/fiber/v2"
)

const secret = "your-secure-secret"

func computeHmac(message string) string {
	h := hmac.New(sha256.New, []byte(secret))
	h.Write([]byte(message))
	// Use a fixed encoding; here we use hex for simplicity
	return fmt.Sprintf("%x", h.Sum(nil))
}

func verifyHmac(r *http.Request, expected string) bool {
	msg := r.Method + "|" + r.URL.Path + "|" + r.URL.RawQuery
	actual := computeHmac(msg)
	return hmac.Equal([]byte(actual), []byte(expected))
}

func safeFileHandler(baseDir string) fiber.HandlerFunc {
	return func(c *fiber.Ctx) error {
		// Expects a query parameter "file"; ensure it's present
		fileQuery := c.Query("file")
		if fileQuery == "" {
			return c.Status(http.StatusBadRequest).SendString("missing file parameter")
		}

		// Verify Hmac signature from a custom header
		sig := c.Get("X-Request-Signature")
		if sig == "" || !verifyHmac(c.Request(), sig) {
			return c.Status(http.StatusUnauthorized).SendString("invalid signature")
		}

		// Build canonical path: join with base, then clean
		candidate := filepath.Join(baseDir, fileQuery)
		cleanPath := filepath.Clean(candidate)

		// Ensure the resolved path stays inside baseDir
		rel, err := filepath.Rel(baseDir, cleanPath)
		if err != nil || strings.HasPrefix(rel, ".."+string(filepath.Separator)) || rel == ".." {
			return c.Status(http.StatusForbidden).SendString("invalid path")
		}

		// Open and serve the file using the cleaned path
		f, err := openFileSafely(cleanPath) // implement openFileSafely with os.Open and os.Stat
		if err != nil {
			return c.Status(http.StatusNotFound).SendString("file not found")
		}
		defer f.Close()
		return c.SendStream(f, filepath.Base(cleanPath))
	}
}

// openFileSafely opens a file and ensures it's a regular file (not a symlink to a device, etc.)
func openFileSafely(path string) (*os.File, error) {
	info, err := os.Lstat(path)
	if err != nil {
		return nil, err
	}
	// Reject symlinks to prevent symlink-based path escape
	if info.Mode()&os.ModeSymlink != 0 {
		return nil, fmt.Errorf("symlinks not allowed")
	}
	return os.Open(path)
}

Key points in the remediation:

  • Hmac is computed over the request method, path, and query to bind the signature to the intended request shape.
  • File paths are constructed with filepath.Join and sanitized with filepath.Clean.
  • Before opening, the resolved path is checked to ensure it remains within the base directory using filepath.Rel.
  • Symlinks are explicitly rejected at the file-open step to prevent symlink-based attacks even if the path passes directory checks.

This approach keeps file access deterministic and prevents attackers from leveraging server-side symlinks to bypass path-based authorization while still benefiting from Hmac Signatures for request integrity.

Frequently Asked Questions

Why does Hmac verification alone not prevent symlink-based file access attacks?
Hmac Signatures ensure the request has not been altered, but they do not enforce file system boundaries. If the server resolves a user-supplied path after verifying the signature—especially through symlinks—the actual accessed file can differ from what the signature intended. The signature covers the input, not the resolved path, so path validation must be enforced separately.
Can I rely on Clean and Join to fully prevent path traversal in Fiber handlers?
Clean and Join normalize paths but do not guarantee the result is within a base directory if the base is not explicitly checked. Always confirm the cleaned path is prefixed with the base directory (e.g., using filepath.Rel) and reject any paths that escape with .. or symlinks before opening files.