Broken Object Property Level Authorization

Broken Object Property Level Authorization in Go (Gin) [GHSA-qh3j-mrg8-f234]

[Updated Apr 2026] Updated GHSA-qh3j-mrg8-f234

Overview

Broken Object Property Level Authorization (BOPLA) vulnerabilities in API services allow attackers to access, modify, or delete resources that belong to other users by manipulating object identifiers in requests. In real-world Go (Gin) services, this can lead to data breaches, exposure of sensitive information, and unauthorized actions, potentially escalating to broader privilege abuse. This guide addresses the pattern in general, noting that no CVEs are referenced here, and focuses on how the vulnerability manifests and how to remediate. In Gin-based applications, endpoints frequently fetch resources by ID from the path and return them without verifying that the authenticated user owns the resource or has permission to act on it. Attackers can enumerate IDs (for example via /resources/{id}) and observe access outcomes, enabling read, update, or delete operations on data they should not control when per-object checks are missing. The vulnerability stems from authenticating a user but not enforcing per-object authorization in code or data access. Go (Gin) handlers may call a DB lookup by ID and return the result directly, bypassing ownership or permission checks, especially when authorization logic is added only at a high level or is incomplete. This pattern is common in RESTful endpoints that rely on client-supplied IDs without binding them to the caller's permissions. Remediation focuses on enforcing object-level access control, binding every resource operation to the authenticated principal, and returning appropriate errors without leaking resource existence. Use middleware to inject the user identity into the request, enforce ownership in the data layer or service layer, and add tests for ID enumeration scenarios. Consider RBAC/ABAC, token scopes, and database-level protections as part of a defense-in-depth strategy.

Code Fix Example

Go (Gin) API Security Remediation
Vulnerable:
package main
import (
  "net/http"
  "github.com/gin-gonic/gin"
)
type Resource struct { ID string; OwnerID string; Data string }
var db = map[string]Resource{
  `1`: { ID: `1`, OwnerID: `alice`, Data: `d1` },
  `2`: { ID: `2`, OwnerID: `bob`, Data: `d2` },
}
func vulnerableGet(c *gin.Context) {
  id := c.Param(`id`)
  if r, ok := db[id]; ok {
    c.JSON(http.StatusOK, r)
    return
  }
  c.Status(http.StatusNotFound)
}
func main() {
  r := gin.Default()
  r.GET(`/resources/:id`, vulnerableGet)
  _ = r.Run(`:8080`)
}

Fixed:
package main
import (
  "net/http"
  "github.com/gin-gonic/gin"
)
type Resource struct { ID string; OwnerID string; Data string }
var db = map[string]Resource{
  `1`: { ID: `1`, OwnerID: `alice`, Data: `d1` },
  `2`: { ID: `2`, OwnerID: `bob`, Data: `d2` },
}
func fixedGet(c *gin.Context) {
  user, exists := c.Get(`userID`)
  if !exists {
    c.Status(http.StatusUnauthorized)
    return
  }
  uid := user.(string)
  id := c.Param(`id`)
  if r, ok := db[id]; ok {
    if r.OwnerID == uid {
      c.JSON(http.StatusOK, r)
      return
    }
    c.Status(http.StatusForbidden)
    return
  }
  c.Status(http.StatusNotFound)
}
func main() {
  r := gin.Default()
  r.GET(`/secure/res/:id`, fixedGet)
  _ = r.Run(`:8080`)
}

CVE References

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