HIGH insecure direct object referencefibermongodb

Insecure Direct Object Reference in Fiber with Mongodb

Insecure Direct Object Reference in Fiber with Mongodb — how this specific combination creates or exposes the vulnerability

Insecure Direct Object Reference (BOLA/IDOR) occurs when an API exposes internal object references—such as a MongoDB _id or a numeric index—and allows an authenticated or unauthenticated caller to manipulate that reference to access or modify data they should not see. In a Go Fiber backend using MongoDB as the datastore, this commonly arises when route parameters (e.g., /users/:id or /api/documents/:docId) are passed directly into database queries without ensuring the requesting subject has permission for that specific object.

Consider a Fiber endpoint that retrieves a user profile by ID:

// Example: vulnerable endpoint in Fiber with MongoDB
app.Get('/users/:userID', func(c *fiber.Ctx) error {
    userID := c.Params('userID')
    var user bson.M
    if err := usersCollection.FindOne(c.Context(), bson.M{"_id": userID}).Decode(&user); err != nil {
        return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": "not found"})
    }
    return c.JSON(user)
})

If the userID is a plain string or ObjectID taken directly from the URL and used as a filter in MongoDB, there is no check that the authenticated caller is allowed to view that user. An attacker who knows or guesses another user’s ID can enumerate profiles freely. This is a classic BOLA/IDOR: the reference (the ID) is predictable and not bound to an authorization check.

MongoDB itself does not enforce application-level permissions; it only matches the query filter. Therefore, if your Fiber routes do not implement per-request authorization—verifying that the authenticated subject owns or is permitted to interact with the referenced document—MongoDB will happily return or update whatever document matches the provided ID. Insecure use of indexes (numeric positions in arrays) or non-unique fields can similarly expose references that should be opaque. Even with authentication in place (e.g., JWT verified by Fiber middleware), missing authorization logic means the authentication boundary is bypassed horizontally (same privilege, different user) or vertically (lower to higher privilege).

Another common pattern is using a sequential integer as a business document ID (e.g., order IDs) while storing records in MongoDB with ObjectID primary keys. If the endpoint translates orderID=42 into an internal MongoDB query without verifying that the authenticated tenant or user is allowed to access order 42, the attacker can iterate through integers to access other tenants’ orders. This is especially risky when combined with overly permissive read filters like {} or when indexes are not aligned with authorization boundaries.

SSRF and related client-side attacks are orthogonal but worth noting: if your MongoDB connection string or local endpoints are exposed via server-side logic that also suffers from IDOR, an attacker may leverage BOLA/IDOR to probe internal services or metadata. The key takeaway is that IDOR in this stack is not about MongoDB being misconfigured—it is about the application layer failing to enforce authorization on object references before constructing MongoDB queries.

Mongodb-Specific Remediation in Fiber — concrete code fixes

Remediation centers on enforcing authorization at the point where the route parameter becomes a MongoDB query. Instead of using the raw parameter directly, bind the reference to the requesting subject and scope the query accordingly.

1. Scope queries to the authenticated subject. Ensure every query includes a tenant or user identifier derived from the authenticated context, not just the user-supplied ID.

// Secure: scope by authenticated subject (e.g., from JWT claims)
app.Get('/users/:userID', func(c *fiber.Ctx) error {
    userID := c.Params('userID')
    claims := c.Locals("claims").(jwtClaims)
    // Require that the requested userID matches the subject in the token
    if claims.Subject != userID {
        return c.Status(fiber.StatusForbidden).JSON(fiber.Map{"error": "forbidden"})
    }
    var user bson.M
    if err := usersCollection.FindOne(c.Context(), bson.M{"_id": userID, "tenantId": claims.TenantID}).Decode(&user); err != nil {
        return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": "not found"})
    }
    return c.JSON(user)
})

2. Use indirect references (mapping table) rather than direct ObjectID exposure. Store an opaque, unguessable reference in your Fiber routes and map it to the MongoDB _id server-side after authorization.

// Secure: indirect reference via a mapping collection
app.Get('/documents/:ref', func(c *fiber.Ctx) error {
    ref := c.Params('ref')
    var mapping struct {
        DocID primitive.ObjectID `bson:"docId"`
        Owner string `bson:"ownerId"`
    }
    if err := mappingsCollection.FindOne(c.Context(), bson.M{"ref": ref}).Decode(&mapping); err != nil {
        return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": "not found"})
    }
    // Re-authorize: ensure the caller can access this document
    claims := c.Locals("claims").(jwtClaims)
    if mapping.Owner != claims.Subject {
        return c.Status(fiber.StatusForbidden).JSON(fiber.Map{"error": "forbidden"})
    }
    var doc bson.M
    if err := docsCollection.FindOne(c.Context(), bson.M{"_id": mapping.DocID}).Decode(&doc); err != nil {
        return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "server error"})
    }
    return c.JSON(doc)
})

3. Avoid exposing sequential or guessable identifiers in URLs. If using MongoDB ObjectID is acceptable, prefer it over integers for public-facing references, but still scope by tenant or ownership fields. If you must use integers, introduce a UUID or token mapping as above.

4. Validate and sanitize inputs before using them in MongoDB filters. Even with authorization, ensure the parameter is of the expected type (e.g., valid ObjectID) to avoid injection or unexpected type coercion. Use strict JSON Schema or custom validation in your Fiber handlers.

// Validate ObjectID format before querying
import "go.mongodb.org/mongo-driver/bson/primitive"

app.Get('/items/:itemID', func(c *fiber.Ctx) error {
    itemID := c.Params('itemID')
    oid, err := primitive.ObjectIDFromHex(itemID)
    if err != nil {
        return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "invalid id"})
    }
    // Proceed with scoped query
    claims := c.Locals("claims").(jwtClaims)
    var item bson.M
    if err := itemsCollection.FindOne(c.Context(), bson.M{"_id": oid, "tenant": claims.TenantID}).Decode(&item); err != nil {
        return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": "not found"})
    }
    return c.JSON(item)
})

These patterns align with remediations for findings mapped to frameworks such as OWASP API Top 10 (2023) A1: Broken Object Level Authorization. By coupling route parameters with tenant or ownership checks and avoiding direct exposure of MongoDB internal keys, you reduce the attack surface for IDOR in a Fiber + MongoDB stack.

Related CWEs: bolaAuthorization

CWE IDNameSeverity
CWE-250Execution with Unnecessary Privileges HIGH
CWE-639Insecure Direct Object Reference CRITICAL
CWE-732Incorrect Permission Assignment HIGH

Frequently Asked Questions

Does using MongoDB ObjectID instead of integers fully prevent IDOR?
No. ObjectIDs are harder to guess than sequential integers, but they are not a substitute for authorization. Without scoping queries to the authenticated subject or tenant, an attacker who obtains a valid ObjectID can still access unauthorized documents. Always enforce per-request ownership or tenant checks.
How should I handle references for shared resources (e.g., public documents) while preventing IDOR?
Use a permission model and scope queries to reflect access rights. For publicly readable documents, you can omit ownership checks but should still validate the reference format and enforce rate limits. For restricted documents, map references to access control lists (ACLs) or roles and ensure every MongoDB query includes those constraints.