Broken Object Level Authorization

Broken Object Level Authorization in Go (Gin) [May 2026] [GHSA-pq86-j2c2-47f6]

[Updated May 2026] Updated GHSA-pq86-j2c2-47f6

Overview

Broken Object Level Authorization (BOLA) vulnerabilities occur when an API exposes resources to users beyond their permissions. In Gin-based Go applications, endpoints that rely on an object ID from the path or query without validating the owning user's rights can allow attackers to access or modify other users' data. This class of flaw enables resource enumeration, data leakage, and unauthorized actions, undermining privacy, regulatory compliance, and trust in the service. In real-world Go (Gin) apps, developers may implement authentication to identify the user but skip per-object authorization when handling resources fetched by ID. A common bug pattern is to take an ID from c.Param("doc_id") and fetch the resource without checking whether resource.OwnerID matches the authenticated user's ID, or without enforcing ownership at the data layer. Attackers can iterate IDs and perform read, update, or delete actions on other users' resources, leading to data exposure and potential data integrity issues. Remediation requires consistent per-object authorization. Use middleware to attach the authenticated user to the request context, implement a reusable authorization check comparing resource.OwnerID to user.ID, and apply it in every handler touching user-owned resources. Prefer data-layer constraints and explicit RBAC/ABAC policies, and write tests that simulate both authorized and unauthorized access, to prevent ID-based access to unrelated resources. Note: This guide does not reference any specific CVEs here; it documents a common pattern and its mitigations. By enforcing ownership checks and avoiding reliance on a user identity alone for access decisions, you mitigate typical BOLA risks in Gin-based Go services.

Code Fix Example

Go (Gin) API Security Remediation
// Vulnerable and fixed in one app using mode toggle
package main

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

type User struct {
	ID string
}

type Document struct {
	ID      string
	OwnerID string
	Content string
}

var documents = []Document{
	{ID: "doc1", OwnerID: "alice", Content: "Alice's doc"},
	{ID: "doc2", OwnerID: "bob", Content: "Bob's doc"},
}

// Auth middleware to populate user from header
func AuthMiddleware() gin.HandlerFunc {
	return func(c *gin.Context) {
		userID := c.GetHeader("X-User")
		if userID == "" {
			c.AbortWithStatus(http.StatusUnauthorized)
			return
		}
		c.Set("user", User{ID: userID})
		c.Next()
	}
}

func vulnerableHandler(c *gin.Context) {
	docID := c.Param("doc_id")
	for _, d := range documents {
		if d.ID == docID {
			c.JSON(http.StatusOK, d)
			return
		}
	}
	c.Status(http.StatusNotFound)
}

func fixedHandler(c *gin.Context) {
	docID := c.Param("doc_id")
	var doc *Document
	for i := range documents {
		if documents[i].ID == docID {
			doc = &documents[i]
			break
		}
	}
	if doc == nil {
		c.Status(http.StatusNotFound)
		return
	}
	user := c.MustGet("user").(User)
	if doc.OwnerID != user.ID {
		c.Status(http.StatusForbidden)
		return
	}
	c.JSON(http.StatusOK, doc)
}

func main() {
	r := gin.Default()
	r.Use(AuthMiddleware())
	r.GET("/docs/:doc_id", func(c *gin.Context) {
		if c.Query("mode") == "fixed" {
			fixedHandler(c)
		} else {
			vulnerableHandler(c)
		}
	})
	r.Run()
}

CVE References

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