HIGH ssrf server sidefiberhmac signatures

Ssrf Server Side in Fiber with Hmac Signatures

Ssrf Server Side in Fiber with Hmac Signatures — how this specific combination creates or exposes the vulnerability

Server-side request forgery (SSRF) in Go Fiber applications that use HMAC signatures can arise when a signed route parameter or header is used to select an outbound HTTP target without strict validation. For example, an endpoint may accept a signed url query parameter that is validated with a per-request HMAC before the application performs an HTTP request on the provided value. If the application trusts the signature but does not also enforce a strict allowlist of destinations, an attacker can supply a maliciously signed URL pointing to an internal service (e.g., http://127.0.0.1:8080/metadata) or to cloud metadata endpoints (e.g., http://169.254.169.254/latest/meta-data/), and the HMAC verification will pass because the server generated the signature for the attacker-supplied input.

In Fiber, this often occurs when custom middleware validates an HMAC over query parameters or headers to prevent tampering, but the server-side HTTP client uses the validated parameter directly as the request target. An attacker who cannot forge the HMAC might still exploit business logic that embeds user input into the destination URL, especially if the signature is computed over only a subset of parameters (e.g., an id) while the target host is supplied separately and not validated. A common pattern is a webhook callback where the signature ensures integrity of the callback URL, but the application does not restrict the host or path, enabling SSRF against internal endpoints or third-party services reachable from the server.

The combination of HMAC-based integrity checks and flexible outbound requests can therefore create a false sense of security: the signature prevents tampering with the signed fields, but it does not prevent the signed data from pointing to an undesirable destination. If the application resolves hostnames without restricting schemes, ports, or internal IP ranges, SSRF can be chained with other issues such as insecure deserialization or unsafe consumption of user-controlled URLs. Because SSRF can lead to metadata exposure, port scanning from the server, or access to cloud instance metadata, this pattern demands explicit destination allowlisting and careful schema validation before any network call is made.

Hmac Signatures-Specific Remediation in Fiber — concrete code fixes

To remediate SSRF when HMAC signatures are used in Fiber, ensure that the data used to form the outbound request is not solely derived from the signed input. Apply allowlisting for hosts, ports, and schemes, and treat the HMAC as integrity protection rather than authorization to reach arbitrary destinations. Below are concrete, working examples that demonstrate secure handling in Go Fiber.

Example 1: Signed callback URL with host and scheme allowlist

Compute the HMAC over a subset of parameters, then validate the target host and scheme against an allowlist before performing the request.

package main

import (
	"crypto/hmac"
	"crypto/sha256"
	"encoding/hex"
	"net/url"
	"strings"

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

var secretKey = []byte("super-secret-key")

// allowedHosts is a strict allowlist of destinations
var allowedHosts = map[string]bool{
	"api.example.com": true,
	"webhook.example.com": true,
}

// allowedSchemes restricts protocols
var allowedSchemes = map[string]bool{
	"https": true,
}

func computeHmac(data string) string {
	h := hmac.New(sha256.New, secretKey)
	h.Write([]byte(data))
	return hex.EncodeToString(h.Sum(nil))
}

func verifyHmac(data, receivedMac string) bool {
	expected := computeHmac(data)
	return hmac.Equal([]byte(expected), []byte(receivedMac))
}

func main() {
	app := fiber.New()

	app.Get("/callback", func(c *fiber.Ctx) error {
		rawURL := c.Query("url")
		receivedMac := c.Query("mac")
		dataToSign := "url=" + rawURL

		if !verifyHmac(dataToSign, receivedMac) {
			return c.Status(fiber.StatusBadRequest).SendString("invalid signature")
		}

		parsed, err := url.Parse(rawURL)
		if err != nil {
			return c.Status(fiber.StatusBadRequest).SendString("invalid url")
		}

		if !allowedSchemes[parsed.Scheme] {
			return c.Status(fiber.StatusBadRequest).SendString("scheme not allowed")
		}

		if !allowedHosts[parsed.Host] {
			return c.Status(fiber.StatusBadRequest).SendString("host not allowed")
		}

		// Safe to use parsed.RequestURI() or build a new URL with validated components
		client := &http.Client{}
		req, _ := http.NewRequest(http.MethodGet, parsed.String(), nil)
		resp, err := client.Do(req)
		if err != nil {
			return c.Status(fiber.StatusInternalServerError).SendString("request failed")
		}
		defer resp.Body.Close()

		return c.SendStatus(fiber.StatusOK)
	})

	app.Listen(":3000")
}

Example 2: HMAC over multiple fields with explicit destination

Sign multiple fields including an intended destination ID, then map that ID to a concrete, validated endpoint.

package main

import (
	"crypto/hmac"
	"crypto/sha256"
	"encoding/hex"
	"net/http"

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

var secretKey = []byte("super-secret-key")

var allowedEndpoints = map[string]string{
	"user_profile": "https://api.example.com/profile",
	"account_info": "https://api.example.com/account",
}

func computeHmac(fields ...string) string {
	h := hmac.New(sha256.New, secretKey)
	for _, f := range fields {
		h.Write([]byte(f))
	}
	return hex.EncodeToString(h.Sum(nil))
}

func main() {
	app := fiber.New()

	app.Get("/resource", func(c *fiber.Ctx) error {
		id := c.Query("id")
		endpointKey := c.Query("key")
		receivedMac := c.Query("mac")

		expectedMac := computeHmac(endpointKey, id)
		if !hmac.Equal([]byte(expectedMac), []byte(receivedMac)) {
			return c.Status(fiber.StatusBadRequest).SendString("invalid signature")
		}

		target, ok := allowedEndpoints[endpointKey]
		if !ok {
			return c.Status(fiber.StatusBadRequest).SendString("endpoint not allowed")
		}

		client := &http.Client{}
		req, _ := http.NewRequest(http.MethodGet, target+"?id="+id, nil)
		resp, err := client.Do(req)
		if err != nil {
			return c.Status(fiber.StatusInternalServerError).SendString("request failed")
		}
		defer resp.Body.Close()

		return c.SendStatus(fiber.StatusOK)
	})

	app.Listen(":3000")
}

These examples emphasize that HMACs should protect integrity of selected parameters while the application independently validates the destination. By combining signature verification with host/scheme allowlists and explicit endpoint mapping, SSRF risks are substantially reduced without removing HMAC-based integrity checks.

Frequently Asked Questions

Can an attacker bypass HMAC verification to exploit SSRF?
Not if the server validates the signature over the exact data it uses for the request and does not allow the attacker to control the destination host, port, or scheme. Ensure the signed payload includes or tightly constrains the target endpoint and perform strict allowlist checks before initiating any network call.
What additional measures reduce SSRF risk when using HMAC signatures in Fiber?
Use explicit destination allowlists (host, port, scheme), avoid forwarding raw user input to HTTP clients, prefer URL parsing over string concatenation, and enforce timeouts and network segmentation for outbound requests. Treat HMAC as integrity protection, not as authorization to reach arbitrary resources.