Broken Object Level Authorization

Broken Object Level Authorization in Go (Gin) [GHSA-xp9r-prpg-373r]

[Updated Mar 2026] Updated GHSA-xp9r-prpg-373r

Overview

Broken Object Level Authorization (BOLA) occurs when an API exposes object references (IDs) and trusts the client to supply valid IDs without verifying ownership for each object. In real-world Go applications using Gin, this translates to handlers that fetch resources by ID and return them if the ID exists, without checking if the authenticated user should access that object. Attackers can sequentially change IDs in the URL to enumerate or access others' data, including sensitive records, messages, or configurations. The impact ranges from data leakage to irreversible modifications and can corrupt audit trails. When CVEs are not provided for this guide, the risk is still real and widely observed in multi-tenant or peer-to-peer APIs. In Gin-based Go services, object-level authorization often manifests as a missing per-object check in handlers, weak or absent owner checks, and inconsistent enforcement across endpoints. Common patterns include endpoints like GET /resources/:id, PUT /resources/:id, or DELETE /resources/:id where only the resource ID is used to fetch data and no ownership validation is performed. Middleware may set a user identity, but developers forget to actually apply that identity to the resource access decisions. This allows attackers to iterate IDs and access or alter data they should not own. Remediation patterns emphasize defense in depth: perform verify ownership for every object in every operation, apply deny-by-default, scope listing results, and test the authorization logic. In Go with Gin, implement a middleware to populate user identity, then in each handler compare Resource.OwnerID with the authenticated user. Prefer explicit 403 responses for unauthorized access and write unit/integration tests that simulate ID tampering. Consider using a repository method that enforces access control at the DB layer (e.g., where OwnerID = ?), or API layer wrappers that centralize authorization checks.

Code Fix Example

Go (Gin) API Security Remediation
package main

import (
  "net/http"
  "github.com/gin-gonic/gin"
)

type Resource struct {
  ID      string
  OwnerID string
  Data    string
}

var resources = map[string]Resource{
  "1": {ID: "1", OwnerID: "alice", Data: "secret1"},
  "2": {ID: "2", OwnerID: "bob", Data: "secret2"},
}

func mockAuthMiddleware() gin.HandlerFunc {
  return func(c *gin.Context) {
    user := c.GetHeader("X-User")
    if user == "" {
      user = "anonymous"
    }
    c.Set("userID", user)
    c.Next()
  }
}

// Vulnerable: returns resource regardless of ownership
func getResourceVulnerable(c *gin.Context) {
  id := c.Param("id")
  if res, ok := resources[id]; ok {
    c.JSON(http.StatusOK, res)
    return
  }
  c.JSON(http.StatusNotFound, gin.H{"error": "not found"})
}

// Fixed: checks ownership before returning
func getResourceFixed(c *gin.Context) {
  id := c.Param("id")
  userID, _ := c.Get("userID")
  if res, ok := resources[id]; ok {
    if res.OwnerID != userID {
      c.JSON(http.StatusForbidden, gin.H{"error": "forbidden"})
      return
    }
    c.JSON(http.StatusOK, res)
    return
  }
  c.JSON(http.StatusNotFound, gin.H{"error": "not found"})
}

func main() {
  r := gin.Default()
  r.Use(mockAuthMiddleware())
  r.GET("/resources/:id/vulnerable", getResourceVulnerable)
  r.GET("/resources/:id/fixed", getResourceFixed)
  r.Run(":8080")
}

CVE References

Choose which optional cookies to allow. You can change this any time.