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 ID | Name | Severity |
|---|---|---|
| CWE-915 | Mass Assignment | HIGH |