Broken Object Property Level Authorization

Broken Object Property Level Authorization in Go (Gin) [GHSA-jh9g-8jqw-m2qx]

[Updated May 2026] Updated GHSA-jh9g-8jqw-m2qx

Overview

Broken Object Property Level Authorization vulnerabilities occur when an API exposes object IDs and relies on client-supplied data to authorize access, often by checking only a property rather than ownership. In Go applications using Gin, endpoints frequently fetch resources by ID from the URL and then perform a permissive check, or skip ownership verification entirely. Impact from this class of vulnerability includes data leakage, privacy violations, and potential data tampering. An attacker can enumerate object IDs (for example, by sequential IDs or guessable identifiers) and retrieve or modify data belonging to other users if proper ownership checks are not enforced at the object level. In Gin-based services, this manifests when handlers return resources by ID without validating that the authenticated user actually owns the resource, or when checks rely on a resource property that can be spoofed or manipulated by the client. This can lead to cross-user access across legacy endpoints or multi-tenant boundaries. Remediation requires enforcing robust, centralized authorization logic that ties every object access to explicit ownership or granted permissions rather than trusting object properties alone. No CVE IDs are provided for this general guide. If CVEs become available, reference them here and map guidance to the affected Gin patterns accordingly.

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{
  "r1": {ID: "r1", OwnerID: "u1", Data: "secret1"},
  "r2": {ID: "r2", OwnerID: "u2", Data: "secret2"},
}

// Vulnerable: returns resource by id without checking ownership
func vulnerableGetResource(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: verify ownership before returning resource
func fixedGetResource(c *gin.Context) {
  id := c.Param("id")
  userID := c.GetHeader("X-User-ID")
  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.GET("/vulnerable/resources/:id", vulnerableGetResource)
  r.GET("/secure/resources/:id", fixedGetResource)
  r.Run()
}

CVE References

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