HIGH mass assignmentfiberhmac signatures

Mass Assignment in Fiber with Hmac Signatures

Mass Assignment in Fiber with Hmac Signatures — how this specific combination creates or exposes the vulnerability

Mass Assignment occurs when an API binds incoming JSON directly to a server-side struct or model without filtering which fields are accepted. In the Fiber web framework for Go, this commonly happens when developers use context.BodyParser() to map request payloads into structs that contain sensitive or derived fields. If the struct includes fields such as Role, IsAdmin, or PermissionLevel, an attacker who can influence the request body can set these values and elevate privileges.

Combining this pattern with Hmac Signatures introduces a nuanced risk. Hmac Signatures are often used to guarantee integrity and authenticity of the request body: the client computes a signature over the payload using a shared secret and sends it in a header (for example, X-Signature). The server recomputes the Hmac over the received body and compares it to the provided signature. When the signature validates successfully, the server may assume the body has not been tampered with and proceed to bind it directly. Because the signature only protects integrity and authenticity, it does not limit which fields are bound. An attacker can still provide a maliciously crafted body that maps to sensitive fields as long as they can guess or obtain the shared secret, or if the secret is weak or leaked. In practice, this combination can lead to scenarios where an API validates Hmac Signatures but still performs unsafe binding, effectively bypassing integrity controls that are assumed to enforce authorization.

Real-world attack patterns mirror findings in OWASP API Top 10 and property authorization checks: an unauthenticated or low-privilege caller discovers an endpoint that parses trusted user input into an admin struct, and if Hmac is used incorrectly (for example, signing only a subset of fields or including mutable fields in the signed payload), the attacker can modify those fields and the signature remains valid. This underscores why frameworks like Fiber require explicit field filtering or selective binding even when Hmac Signatures are in place.

Hmac Signatures-Specific Remediation in Fiber — concrete code fixes

To mitigate Mass Assignment when using Hmac Signatures in Fiber, ensure that only intended fields are bound and that the signature scope is explicit and minimal. Do not rely on signature validation alone to enforce authorization. Instead, use selective binding and strict validation before parsing.

Example 1: Safe binding with explicit struct and Hmac verification

Define an input struct that contains only the fields you expect from the client, and a separate domain struct for internal use that adds sensitive fields with default or server-side values. Verify the Hmac over the raw body before binding.

//go
package main

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

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

const sharedSecret = "super-secret-shared-key" // in practice load from env or secret manager

// Input struct: only fields the client is allowed to set
type CreateUserInput struct {
	Username string `json:"username"`
	Email    string `json:"email"`
	// Do not include Role or IsAdmin here
}

// Internal domain struct
type User struct {
	Username string
	Email    string
	Role     string
}

func verifyHmac(body, receivedSig string) bool {
	h := hmac.New(sha256.New, []byte(sharedSecret))
	h.Write([]byte(body))
	expected := hex.EncodeToString(h.Sum(nil))
	return hmac.Equal([]byte(expected), []byte(receivedSig))
}

func createUser(c *fiber.Ctx) error {
	// Read raw body for signature verification
	rawBody := c.Request().Body()
	// Note: in real apps you may need to buffer and restore body for further parsing
	// For simplicity, we read it once and reuse a copy
	// Assume X-Signature header is present
	sig := string(c.Request().Header.Peek("X-Signature"))
	if !verifyHmac(string(rawBody), sig) {
		return errors.New("invalid signature")
	}

	var input CreateUserInput
	if err := c.BodyParser(&input); err != nil {
		return c.Status(http.StatusBadRequest).JSON(fiber.Map{"error": "invalid payload"})
	}

	serverUser := User{
		Username: input.Username,
		Email:    input.Email,
		Role:     "user", // assigned server-side
	}
	// proceed to store serverUser
	return c.JSON(serverUser)
}

This pattern ensures that even if an attacker manages to include extra fields in the JSON, they are ignored by the binding step because the input struct does not contain them. The Hmac signature is computed over the raw body before parsing, preventing tampering with any field, but authorization is enforced by the server-side struct.

Example 2: Using a whitelist approach with explicit parsing

For more control, unmarshal into a map or a flexible intermediate structure, explicitly copy allowed fields, and reject unexpected keys. This defends against unknown or future fields that could be added by attackers.

//go
package main

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

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

var allowed = map[string]bool{
	"username": true,
	"email":    true,
}

func verifyHmac(body, receivedSig string) bool {
	h := hmac.New(sha256.New, []byte(sharedSecret))
	h.Write([]byte(body))
	expected := hex.EncodeToString(h.Sum(nil))
	return hmac.Equal([]byte(expected), []byte(receivedSig))
}

func createUserWhitelist(c *fiber.Ctx) error {
	rawBody := c.Request().Body()
	sig := string(c.Request().Header.Peek("X-Signature"))
	if !verifyHmac(string(rawBody), sig) {
		return errors.New("invalid signature")
	}

	var raw map[string]json.RawMessage
	if err := json.Unmarshal(rawBody, &raw); err != nil {
		return c.Status(http.StatusBadRequest).JSON(fiber.Map{"error": "invalid json"})
	}

	// Whitelist allowed fields only
	var input CreateUserInput
	for key := range raw {
		if !allowed[key] {
			return c.Status(http.StatusBadRequest).JSON(fiber.Map{"error": "field not allowed"})
		}
	}

	if err := json.Unmarshal(rawBody, &input); err != nil {
		return c.Status(http.StatusBadRequest).JSON(fiber.Map{"error": "invalid payload"})
	}

	serverUser := User{
		Username: input.Username,
		Email:    input.Email,
		Role:     "user",
	}
	return c.JSON(serverUser)
}

In both examples, Hmac Signatures are used to ensure the request body has not been altered, but they do not replace explicit field filtering. By combining runtime signature verification with strict input schemas and whitelisting, you reduce the risk of Mass Assignment while preserving integrity checks.

Related CWEs: propertyAuthorization

CWE IDNameSeverity
CWE-915Mass Assignment HIGH

Frequently Asked Questions

Does Hmac Signatures alone prevent Mass Assignment in Fiber APIs?
No. Hmac Signatures verify integrity and authenticity of the raw request body, but they do not restrict which fields are bound to server-side structs. Without explicit field filtering or whitelisting, an attacker can still set sensitive fields if they can include them in the payload, even when the signature validates.
How should I scope Hmac Signatures to reduce risk alongside input validation in Fiber?
Scope the Hmac over the exact bytes you intend to validate and avoid signing large mutable payloads that include server-added fields. Combine signature checks with strict input structs or a whitelist approach so that only expected fields are bound; treat the signature as integrity assurance, not authorization.