Insecure Direct Object Reference in Fiber
How Insecure Direct Object Reference Manifests in Fiber
Insecure Direct Object Reference (IDOR) in Fiber applications typically occurs when user input is directly used to access resources without proper authorization checks. This vulnerability is particularly common in Fiber's handler functions where developers assume that if a user can see an ID, they should have access to the resource.
A classic Fiber IDOR pattern looks like this:
app.Get("/api/users/:id", func(c *fiber.Ctx) error {
id := c.Params("id")
user, err := getUserByID(id)
if err != nil {
return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": "User not found"})
}
return c.JSON(user)
})The critical flaw here is that the handler retrieves any user by ID without verifying whether the requesting user has permission to view that specific user's data. An authenticated attacker can simply increment the ID parameter to access other users' information.
Another Fiber-specific manifestation occurs with path traversal combined with IDOR:
app.Get("/api/files/:filename", func(c *fiber.Ctx) error {
filename := c.Params("filename")
content, err := os.ReadFile("/var/files/" + filename)
if err != nil {
return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": "File not found"})
}
return c.JSON(fiber.Map{"content": content})
})This pattern is dangerous because it allows direct file access without validating the user's permissions. An attacker could request ../../../etc/passwd or access files belonging to other users.
Database IDOR in Fiber often appears with ORM operations:
app.Get("/api/orders/:id", func(c *fiber.Ctx) error {
id := c.Params("id")
order := models.Order{}
db.First(&order, id)
return c.JSON(order)
})Even though the database query works correctly, there's no check to ensure the authenticated user owns this order. Any authenticated user can view any order by changing the ID parameter.
Fiber's middleware-based architecture can exacerbate IDOR if authentication middleware doesn't properly set user context. A common anti-pattern:
app.Use(authMiddleware)
app.Get("/api/profile/:id", func(c *fiber.Ctx) error {
id := c.Params("id")
// Missing: userID := c.Locals("userID")
// Missing: if id != userID { return c.Status(fiber.StatusForbidden) }
profile, err := getProfileByID(id)
if err != nil {
return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": "Profile not found"})
}
return c.JSON(profile)
})The authentication middleware sets user context in c.Locals, but the handler fails to compare the requested resource ID with the authenticated user's ID, creating an IDOR vulnerability.
Fiber-Specific Detection
Detecting IDOR vulnerabilities in Fiber applications requires both manual code review and automated scanning. middleBrick's black-box scanning approach is particularly effective for Fiber APIs because it tests the actual runtime behavior without needing source code access.
For manual detection in Fiber applications, look for these patterns:
Direct parameter usage without authorization:
app.Get("/api/resources/:id", func(c *fiber.Ctx) error {
id := c.Params("id")
resource := getResourceByID(id) // No permission check!
return c.JSON(resource)
})Missing ownership verification:
app.Get("/api/users/:id/profile", func(c *fiber.Ctx) error {
id := c.Params("id")
// Should verify: if id != c.Locals("userID") { return forbidden }
profile := getUserProfile(id)
return c.JSON(profile)
})Database operations without user scoping:
app.Get("/api/posts/:id", func(c *fiber.Ctx) error {
id := c.Params("id")
post := models.Post{}
db.First(&post, id) // No WHERE user_id = ? clause
return c.JSON(post)
})middleBrick scans Fiber APIs by automatically testing these patterns. It attempts authenticated requests with manipulated IDs to detect whether resources can be accessed across user boundaries. The scanner tests both numeric ID sequences (incrementing IDs to find adjacent records) and parameterized endpoints that might expose other users' data.
For Fiber applications, middleBrick specifically checks:
- Parameter tampering on all authenticated endpoints
- Cross-user data access attempts
- Path traversal vulnerabilities in file-serving endpoints
- Database query patterns that might expose unauthorized records
- Missing authorization checks in middleware chains
- API endpoints that accept user IDs without validation
The scanner's 12 parallel security checks include BOLA (Broken Object Level Authorization) testing, which is the formal category for IDOR vulnerabilities. middleBrick attempts to access resources belonging to other users by systematically varying input parameters and analyzing the responses for data leakage.
For Fiber developers using OpenAPI specifications, middleBrick can analyze your .yaml or .json spec files to identify endpoints that might be vulnerable to IDOR based on their parameter patterns and authentication requirements.
Fiber-Specific Remediation
Remediating IDOR vulnerabilities in Fiber applications requires implementing proper authorization checks and resource scoping. Here are Fiber-specific remediation patterns:
1. User Context Middleware:
func setUserContext(next fiber.Handler) fiber.Handler {
return func(c *fiber.Ctx) error {
userID := c.Locals("userID")
if userID == nil {
return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"error": "Authentication required"})
}
// Store user object for downstream handlers
user := getUserByID(userID.(string))
c.Locals("user", user)
return next(c)
}
}
app.Use(setUserContext)
// Now all handlers can access c.Locals("user")
app.Get("/api/profile", func(c *fiber.Ctx) error {
user := c.Locals("user").(*User)
return c.JSON(user)
})2. Resource Ownership Validation:
func checkResourceOwnership(next fiber.Handler) fiber.Handler {
return func(c *fiber.Ctx) error {
resourceID := c.Params("id")
userID := c.Locals("userID").(string)
owns, err := userOwnsResource(userID, resourceID)
if err != nil || !owns {
return c.Status(fiber.StatusForbidden).JSON(fiber.Map{"error": "Access denied"})
}
return next(c)
}
}
// Apply to specific routes
app.Get("/api/orders/:id", checkResourceOwnership, func(c *fiber.Ctx) error {
id := c.Params("id")
order := getOrderByID(id)
return c.JSON(order)
})3. Database Query Scoping:
app.Get("/api/orders", func(c *fiber.Ctx) error {
userID := c.Locals("userID").(string)
var orders []models.Order
db.Where("user_id = ?", userID).Find(&orders)
return c.JSON(orders)
})
app.Get("/api/orders/:id", func(c *fiber.Ctx) error {
id := c.Params("id")
userID := c.Locals("userID").(string)
var order models.Order
result := db.Where("id = ? AND user_id = ?", id, userID).First(&order)
if result.RowsAffected == 0 {
return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": "Order not found or access denied"})
}
return c.JSON(order)
})4. IDOR Prevention Middleware:
func idorPrevention(next fiber.Handler) fiber.Handler {
return func(c *fiber.Ctx) error {
// Check if route has :id parameter
route := c.Route()
for _, param := range route.Params {
if param == "id" {
// Get the ID from params
id := c.Params("id")
// Get current user ID from context
userID := c.Locals("userID")
// Check if user owns this resource
if !userHasAccessToResource(userID, id) {
return c.Status(fiber.StatusForbidden).JSON(fiber.Map{"error": "Access denied"})
}
break
}
}
return next(c)
}
}
app.Use(idorPrevention)5. File Access Authorization:
app.Get("/api/files/:filename", func(c *fiber.Ctx) error {
filename := c.Params("filename")
userID := c.Locals("userID").(string)
// Validate filename to prevent path traversal
if strings.Contains(filename, "..") || filepath.IsDir(filename) {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid filename"})
}
// Check if user owns this file
if !userOwnsFile(userID, filename) {
return c.Status(fiber.StatusForbidden).JSON(fiber.Map{"error": "Access denied"})
}
content, err := os.ReadFile(filepath.Join("/var/files", filename))
if err != nil {
return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": "File not found"})
}
return c.JSON(fiber.Map{"content": content})
})6. Using Fiber's Context Features:
app.Get("/api/users/:userId/posts", func(c *fiber.Ctx) error {
requestedUserID := c.Params("userId")
currentUserID := c.Locals("userID").(string)
// Only allow viewing own posts or admin access
if requestedUserID != currentUserID && !c.Locals("isAdmin").(bool) {
return c.Status(fiber.StatusForbidden).JSON(fiber.Map{"error": "You can only view your own posts"})
}
var posts []models.Post
db.Where("user_id = ?", requestedUserID).Find(&posts)
return c.JSON(posts)
})The key remediation principle is always validating that the authenticated user has permission to access the specific resource they're requesting, never assuming that possession of an ID implies authorization.
Related CWEs: bolaAuthorization
| CWE ID | Name | Severity |
|---|---|---|
| CWE-250 | Execution with Unnecessary Privileges | HIGH |
| CWE-639 | Insecure Direct Object Reference | CRITICAL |
| CWE-732 | Incorrect Permission Assignment | HIGH |