Broken Object Level Authorization

Broken Object Level Authorization in Go (Gin) [GHSA-hpgw-ww76-c68r]

[Updated May 2026] Updated GHSA-hpgw-ww76-c68r

Overview

Broken Object Level Authorization (BOLA) vulnerabilities let attackers access or modify other users' resources by manipulating object identifiers in API requests. In Go with Gin, endpoints that take an ID from the URL and return the corresponding resource without verifying ownership can expose sensitive data or allow unauthorized edits across user accounts. Common Gin patterns that enable BOLA include handlers that fetch based on a path parameter and rely solely on authentication state, without enforcing per-resource authorization in the data access path. If your database query uses only the ID or if you retrieve the resource then skip ownership checks, any authenticated user could access someone else's data. Remediation should enforce per-resource authorization in every endpoint that accepts an object ID. Bind user identity via middleware, and either filter by ownerID in the query or verify ownership after retrieval before returning data or performing state-changing actions. Add tests that simulate cross-user access to verify mitigation. Additional best practices include centralizing authorization logic, applying RBAC for privileged actions, implementing logging and alerting for unauthorized access attempts, and scanning code changes with automated security tests to prevent regressions.

Code Fix Example

Go (Gin) API Security Remediation
package main\n\nimport (\n  \"net/http\"\n  \"github.com/gin-gonic/gin\"\n)\n\ntype Resource struct {\n  ID string\n  OwnerID string\n  Data string\n}\n\nvar resources = []Resource{\n  {ID: \"1\", OwnerID: \"100\", Data: \"secret1\"},\n  {ID: \"2\", OwnerID: \"101\", Data: \"secret2\"},\n}\n\nfunc main() {\n  r := gin.Default()\n  // Vulnerable: no ownership check\n  r.GET(\"/resources/:id\", vulnerableResource)\n  // Fixed: checks ownership\n  r.GET(\"/secure/resources/:id\", authorizedResource)\n  r.Run()\n}\n\nfunc getUserIDFromHeader(c *gin.Context) string {\n  return c.GetHeader(\"X-User-ID\")\n}\n\nfunc vulnerableResource(c *gin.Context) {\n  id := c.Param(\"id\")\n  var res *Resource\n  for i := range resources {\n    if resources[i].ID == id {\n      res = &resources[i]\n      break\n    }\n  }\n  if res == nil {\n    c.JSON(http.StatusNotFound, gin.H{\"error\": \"not found\"})\n    return\n  }\n  // BOLA: no ownership check\n  c.JSON(http.StatusOK, res)\n}\n\nfunc authorizedResource(c *gin.Context) {\n  id := c.Param(\"id\")\n  userID := getUserIDFromHeader(c)\n  var res *Resource\n  for i := range resources {\n    if resources[i].ID == id && resources[i].OwnerID == userID {\n      res = &resources[i]\n      break\n    }\n  }\n  if res == nil {\n    c.JSON(http.StatusNotFound, gin.H{\"error\": \"not found or not owned\"})\n    return\n  }\n  c.JSON(http.StatusOK, res)\n}\n

CVE References

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