HIGH data exposurefibergo

Data Exposure in Fiber (Go)

Data Exposure in Fiber with Go — how this specific combination creates or exposes the vulnerability

When building APIs with Fiber in Go, data exposure often stems from how route handlers serialize and transmit structured data. Unlike some frameworks that encourage strict separation, it is easy in Go to pass internal structures directly to JSON serialization without filtering sensitive fields. Because JSON tags control only serialization and do not enforce access control, omitting sensitive fields from serialization logic or relying on frontend filtering leads to unintended data disclosure.

Consider a typical handler that returns a user record:

//go:generate go run github.com/gofiber/fiber/v2/cmd/fiber/v2@latest init
package main

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

type User struct {
	ID       uint   `json:\"id\"`
	Email    string `json:\"email\"`
	Password string `json:\"-\"` // intended to omit from JSON
	APIKey   string `json:\"api_key\"`
}

func main() {
	app := fiber.New()
	app.Get("/users/:id", func(c *fiber.Ctx) error {
		// Simulated DB fetch
		user := User{
			ID:       1,
			Email:    "[email protected]",
			Password: "supersecret",
			APIKey:   "sk_live_abc123",
		}
		return c.JSON(user)
	})
	app.Listen(":3000")
}

In this example, the Password field uses json:"-" to exclude it from serialization, and APIKey is explicitly included. If the developer forgets to remove APIKey from the response or mistakenly includes it during refactoring, the live API will leak credentials to any client. Similarly, structs that embed internal metadata (such as database IDs or version fields) can unintentionally expose implementation details when returned directly.

Data exposure in this context also relates to how responses are scoped to the requester. Without proper authorization checks (e.g., ensuring users can only view their own data), an endpoint like /users/:id may return information belonging to other users. This intersects with BOLA/IDOR considerations, but the root cause is often a handler that serializes a full data model without applying field-level or row-level filtering. Even when using struct embedding or helper serialization functions, missing checks can result in sensitive fields such as tokens, hashes, or internal flags being included in the JSON payload.

The use of pointers and omitempty can also introduce subtle exposure risks. For example:

type SafeUser struct {
	ID    uint   `json:\"id\"`
	Email string `json:\"email\"`
	Token *string `json:\"token,omitempty\"`
}

If Token is a pointer that is non-nil in some code paths, it will be included in the response. In Go, zero values are often omitted with omitempty, but pointers require explicit nil checks. If the handler does not carefully manage token lifetimes and sets them conditionally, the field may appear in responses when it should remain hidden.

Because middleBrick tests the unauthenticated attack surface, it can detect whether sensitive fields are present in JSON responses for public endpoints. Findings typically highlight specific fields (e.g., api_key, password_hash) returned without access controls and recommend removing them from serialization or enforcing strict authorization before data transmission.

Go-Specific Remediation in Fiber

Remediation centers on disciplined struct design, centralized serialization, and explicit authorization. Define separate response structs that include only the fields intended for the client. This prevents accidental inclusion of sensitive fields even if the domain model changes.

Example of a safe, dedicated response struct:

//go:generate go run github.com/gofiber/fiber/v2/cmd/fiber/v2@latest init
package main

import (
	"github.com/gofiber/factor/v2"
)

type User struct {
	ID       uint   `json:\"id\"`
	Email    string `json:\"email\"`
	Password string `json:\"-\"`
	APIKey   string `json:\"-\"`
}

type UserPublic struct {
	ID    uint   `json:\"id\"`
	Email string `json:\"email\"`
}

func currentUser(c *fiber.Ctx) error {
	// In practice, derive userID from authenticated session
	user, err := findUserByID(c.Params(\"id\"))
	if err != nil {
		return c.Status(fiber.StatusNotFound).JSON(fiber.Map{\"error\": \"not found\"})
	}
	return c.JSON(UserPublic{ID: user.ID, Email: user.Email})
}

func findUserByID(id string) (User, error) {
	// Simulated DB lookup
	return User{
		ID:       1,
		Email:    "[email protected]",
		Password: "supersecret",
		APIKey:   "sk_live_abc123",
	}, nil
}

This pattern ensures that only UserPublic is serialized. The sensitive fields on User are tagged with - or omitted, and no accidental refactoring will expose them because they are not present in the response struct.

For endpoints that must return variable fields, use selective population or a whitelist approach. With maps, you can explicitly set allowed keys:

func getUserFields(c *fiber.Ctx) error {
	user, err := findUserByID(c.Params(\"id\"))
	if err != nil {
		return c.Status(fiber.StatusNotFound).JSON(fiber.Map{\"error\": \"not found\"})
	}
	// Explicitly allowlisted fields
	response := map[string]interface{}{
		\"id\":    user.ID,
		\"email\": user.Email,
	}
	return c.JSON(response)
}

Authorization should be checked before serialization. Even when using public structs, verify that the requesting user is permitted to view the target resource:

func getUserProfile(c *fiber.Ctx) error {
	userID := c.Params(\"id\")
	// Assume getAuthenticatedUserID returns the requester's user ID
	requesterID := getAuthenticatedUserID(c)
	if requesterID != userID {
		return c.Status(fiber.StatusForbidden).JSON(fiber.Map{\"error\": \"forbidden\"})
	}
	user, err := findUserByID(userID)
	if err != nil {
		return c.Status(fiber.StatusNotFound).JSON(fiber.Map{\"error\": \"not found\"})
	}
	return c.JSON(UserPublic{ID: user.ID, Email: user.Email})
}

These practices align with checks that middleBrick performs, such as verifying that sensitive fields are not returned on public endpoints and that responses are scoped to the requesting user. By combining strict struct definitions, centralized data transformations, and per-request authorization, you reduce the likelihood of accidental data exposure in Fiber Go services.

Related CWEs: dataExposure

CWE IDNameSeverity
CWE-200Exposure of Sensitive Information HIGH
CWE-209Error Information Disclosure MEDIUM
CWE-213Exposure of Sensitive Information Due to Incompatible Policies HIGH
CWE-215Insertion of Sensitive Information Into Debugging Code MEDIUM
CWE-312Cleartext Storage of Sensitive Information HIGH
CWE-359Exposure of Private Personal Information (PII) HIGH
CWE-522Insufficiently Protected Credentials CRITICAL
CWE-532Insertion of Sensitive Information into Log File MEDIUM
CWE-538Insertion of Sensitive Information into Externally-Accessible File HIGH
CWE-540Inclusion of Sensitive Information in Source Code HIGH

Frequently Asked Questions

Can using json:"-" on sensitive fields guarantee they will never be exposed in a Fiber Go API?
It greatly reduces the risk, but does not guarantee safety if response structs are changed later or if sensitive data is included through embedding or reflection. Prefer dedicated response structs that include only intended fields.
How does middleBrick detect data exposure in unauthenticated scans of Fiber Go endpoints?
middleBrick checks public endpoints for the presence of sensitive fields (such as api_key or password hashes) in JSON responses and flags endpoints that return fields which should be protected by authorization or omitted from serialization.