Broken Object Property Level Authorization

Broken Object Property Level Authorization in Go (Gin) [GHSA-q7f2-rv22-2xgr]

[Updated May 2026] Updated GHSA-q7f2-rv22-2xgr

Overview

Remediation guide for Broken Object Property Level Authorization in Go (Gin). This vulnerability occurs when an API returns object data based solely on an object ID supplied by the client without validating that the caller owns or is permitted to access that specific object. In Go with Gin, this pattern often appears in handlers that fetch a resource by ID and return the object fields directly, effectively using the ID as the only access gate. Attackers can enumerate or guess IDs to retrieve data belonging to other users, leading to unintended data exposure. The risk is particularly acute when sensitive fields or nested properties (for example, owner identifiers, private attributes, or aggregated data) are included in the response and not gated by proper authorization checks.

Code Fix Example

Go (Gin) API Security Remediation
package main

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

type Resource struct {
  ID      int64  `json:"id"`
  OwnerID int64  `json:"owner_id"`
  Data    string `json:"data"`
}

var resources = []Resource{
  {ID: 1, OwnerID: 101, Data: "secret A"},
  {ID: 2, OwnerID: 202, Data: "secret B"},
}

func dbGetResourceByID(id string) (Resource, bool) {
  for _, r := range resources {
    if strconv.FormatInt(r.ID, 10) == id {
      return r, true
    }
  }
  return Resource{}, false
}

// Vulnerable pattern: returns the resource without verifying ownership
func vulnerableHandler(c *gin.Context) {
  // Simulated authenticated user (for demonstration purposes)
  userID := int64(0)
  if v, ok := c.Get("userID"); ok {
    userID = v.(int64)
  }
  id := c.Param("id")
  res, ok := dbGetResourceByID(id)
  if !ok {
    c.JSON(http.StatusNotFound, gin.H{"error": "not found"})
    return
  }
  // No ownership check: this exposes resources owned by others
  _ = userID
  c.JSON(http.StatusOK, res)
}

// Fixed pattern: enforce object-level authorization
func secureHandler(c *gin.Context) {
  userID := int64(0)
  if v, ok := c.Get("userID"); ok {
    userID = v.(int64)
  }
  id := c.Param("id")
  res, ok := dbGetResourceByID(id)
  if !ok {
    c.JSON(http.StatusNotFound, gin.H{"error": "not found"})
    return
  }
  if res.OwnerID != userID {
    c.JSON(http.StatusForbidden, gin.H{"error": "forbidden"})
    return
  }
  c.JSON(http.StatusOK, res)
}

func main() {
  r := gin.Default()
  // Simple fake authentication to illustrate per-request ownership
  r.Use(func(c *gin.Context) {
    // In real apps, extract from JWT or session; here we set a fixed user for demo
    c.Set("userID", int64(101))
    c.Next()
  })
  r.GET("/vuln/resource/:id", vulnerableHandler)
  r.GET("/secure/resource/:id", secureHandler)
  r.Run(":8080")
}

CVE References

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