Out Of Bounds Write in Fiber with Basic Auth
Out Of Bounds Write in Fiber with Basic Auth
An Out Of Bounds Write in a Fiber service using HTTP Basic Authentication occurs when user-controlled input is used to index or size a buffer, array, or slice without proper validation, enabling writes beyond allocated memory boundaries. This note explains how the combination of Fiber, Basic Auth, and unchecked input can expose or amplify the risk.
Basic Authentication in Fiber is typically implemented by inspecting the Authorization header and decoding the base64-encoded credentials. Because this happens before request body parsing, developers may assume the identity is established safely and then process untrusted input without additional constraints. For example, an endpoint may accept a numeric index or a size parameter from the URL, a form body, or JSON, and use that value to index into a fixed-size array or to allocate a buffer. If the parameter is not validated, an attacker can supply a large or negative integer, causing a write past the end of the slice or into memory not intended for user data.
Consider a handler that reads an index from a query parameter and writes user data into a fixed-length array:
// Unsafe: index from query parameter without validation
var buffer [64]byte
c.Get("/write", func(c *fiber.Ctx) error {
idxStr := c.Query("idx")
var idx int
if _, err := fmt.Sscanf(idxStr, "%d", &idx); err != nil {
return c.Status(fiber.StatusBadRequest).SendString("invalid idx")
}
// Potential out-of-bounds write if idx < 0 or idx >= len(buffer)
buffer[idx] = 'X'
return c.SendStatus(fiber.StatusOK)
})
In this scenario, Basic Auth may be enforced via middleware that sets c.Locals("user", user) after verifying credentials. However, the authentication step does not protect the bounds check on buffer. An authenticated attacker can still manipulate idx to write outside the array, potentially corrupting adjacent memory, including other variables or internal runtime structures. In a managed language like Go with a runtime, the immediate effect is typically a panic (index out of range), but in environments that allow unsafe memory access or when interacting with C via cgo, such writes can lead to arbitrary code execution or information disclosure.
Another pattern involves decoding a JSON body into a struct that includes a slice field, then using a user-supplied value to determine the length of a backing array or to copy data into a destination buffer:
type Payload struct {
Data []byte `json:"data"
}
c.Post("/upload", func(c *fiber.Ctx) error {
var p Payload
if err := c.BodyParser(&p); err != nil {
return c.Status(fiber.StatusBadRequest).SendString("invalid body")
}
// Use a query parameter to decide how many bytes to copy
nStr := c.Query("n")
var n int
if _, err := fmt.Sscanf(nStr, "%d", &n); err != nil || n < 0 || n > len(p.Data) {
return c.Status(fiber.StatusBadRequest).SendString("invalid n")
}
dst := make([]byte, 10)
// Out-of-bounds write if n > 10 or n < 0
copy(dst, p.Data[:n])
return c.JSON(fiber.Map{"written": n})
})
Even with a check for n >= 0, failing to ensure n <= len(dst) allows writing beyond the destination slice. Basic Auth may protect this endpoint via a middleware that validates credentials, but it does not mitigate the need to validate n against the destination size. The combination of authenticated access and unchecked input increases the likelihood that malicious payloads reach production handlers, especially if logging or error handling inadvertently exposes sensitive data through out-of-bounds reads.
Remediation focuses on strict input validation and avoiding assumptions about trust boundaries. Authentication confirms identity; it does not imply that input conforms to safety constraints. Always validate numeric indices and sizes against the actual length of buffers or slices, and prefer using slices with dynamic growth or explicit bounds checks before indexing. When copying data, ensure source ranges are strictly limited by the destination capacity.
Basic Auth-Specific Remediation in Fiber
Remediation for Basic Auth scenarios centers on validating all user-controlled data independently of authentication. Below are concrete, safe patterns for implementing Basic Auth in Fiber and preventing out-of-bounds writes.
Secure Basic Auth middleware with strict input validation:
func BasicAuthMiddleware(c *fiber.Ctx) error {
// Extract and validate Authorization header format
auth := c.Get("Authorization")
if len(auth) < 6 || auth[:6] != "Basic " {
return c.Status(fiber.StatusUnauthorized).SendString("auth required")
}
payload, err := base64.StdEncoding.DecodeString(auth[6:])
if err != nil {
return c.Status(fiber.StatusUnauthorized).SendString("invalid auth")
}
// Expect "username:password"
parts := bytes.SplitN(payload, []byte(":"), 2)
if len(parts) != 2 || !validUser(string(parts[0]), string(parts[1])) {
return c.Status(fiber.StatusUnauthorized).SendString("invalid credentials")
}
c.Locals("authenticated", true)
return c.Next()
}
func validUser(user, pass string) bool {
// Compare securely in constant time in production
return user == "admin" && pass == "secret"
}
Validated handler with bounds-safe operations:
c.Post("/safe-write", func(c *fiber.Ctx) error {
// Ensure authentication has passed
if ok := c.Locals("authenticated"); !ok {
return c.Status(fiber.StatusUnauthorized).SendString("unauthorized")
}
idxStr := c.Query("idx")
var idx int
if _, err := fmt.Sscanf(idxStr, "%d", &idx); err != nil {
return c.Status(fiber.StatusBadRequest).SendString("invalid idx")
}
buffer := [64]byte{}
// Explicit bounds check before write
if idx < 0 || idx >= len(buffer) {
return c.Status(fiber.StatusBadRequest).SendString("idx out of range")
}
buffer[idx] = 'X'
return c.JSON(fiber.Map{"ok": true})
})
Safe slice handling with length checks:
c.Post("/safe-copy", func(c *fiber.Ctx) error {
if ok := c.Locals("authenticated"); !ok {
return c.Status(fiber.StatusUnauthorized).SendString("unauthorized")
}
var p Payload
if err := c.BodyParser(&p); err != nil {
return c.Status(fiber.StatusBadRequest).SendString("invalid body")
}
nStr := c.Query("n")
var n int
if _, err := fmt.Sscanf(nStr, "%d", &n); err != nil || n < 0 {
return c.Status(fiber.StatusBadRequest).SendString("invalid n")
}
dst := make([]byte, 10)
// Validate source slice bounds against destination capacity
if n > len(dst) || n > len(p.Data) {
return c.Status(fiber.StatusBadRequest).SendString("n exceeds buffer limits")
}
copy(dst, p.Data[:n])
return c.JSON(fiber.Map{"written": n})
})
These patterns ensure that authentication and input validation are independent concerns. Basic Auth provides identity, but every numeric index, size, or length must be checked against the actual capacity of buffers and slices before use.