Broken Object Property Level Authorization

Broken Object Property Level Authorization in Go (Gin) [GHSA-gj49-89wh-h4gj]

[Updated Jun 2026] Updated GHSA-gj49-89wh-h4gj

Overview

In production environments, Broken Object Property Level Authorization (BOPLA) can expose private resources to unauthorized users simply by guessing or enumerating object IDs surfaced by APIs. If an endpoint returns an object based solely on an identifier provided by the client, without confirming that the caller owns or is permitted to view that object, an attacker can access data belonging to other users. The impact can include leakage of sensitive personal data, documents, or configuration records, potentially leading to regulatory issues and loss of trust. In Go applications using the Gin framework, this class of vulnerability typically shows up when handlers extract an ID from the request path or query and fetch the resource without validating ownership. An auth middleware might set a user identity, but the code path that retrieves and returns the resource neglects to enforce ownership checks, or the check is only partially implemented in a single endpoint. Attackers can enumerate IDs (e.g., /docs/1, /docs/2) and access data they should not see. To prevent BOPLA in Gin, enforce strict ownership checks at the API boundary or deeper in the data access layer. Prefer scoped data queries (WHERE id = ? AND owner_id = ?) or equivalent in your ORM, and ensure that every route that returns an object also validates that the requesting user is the object's owner. Use a single, reusable authorization helper or middleware to avoid inconsistent checks across routes. These changes are complemented by comprehensive tests, including negative tests that exercise unauthorized access and positive tests that confirm authorized access, plus audit logging for failed attempts. Implement defense-in-depth by combining authentication, authorization, and secure coding practices to reduce the risk of BOPLA across services.

Code Fix Example

Go (Gin) API Security Remediation
package main

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

type Item struct { ID int; OwnerID int; Data string }

var items = map[int]Item{
  1: {ID: 1, OwnerID: 1, Data: "secret1"},
  2: {ID: 2, OwnerID: 2, Data: "secret2"},
}

func getCurrentUserID(c *gin.Context) int {
  id, _ := strconv.Atoi(c.GetHeader("X-User-ID"))
  return id
}

// Vulnerable handler: returns resource by ID without ownership check
func vulnerable(c *gin.Context) {
  id, _ := strconv.Atoi(c.Param("id"))
  if it, ok := items[id]; ok {
    c.JSON(http.StatusOK, gin.H{"id": it.ID, "data": it.Data})
    return
  }
  c.Status(http.StatusNotFound)
}

// Fixed handler: enforces ownership before returning resource
func fixed(c *gin.Context) {
  id, _ := strconv.Atoi(c.Param("id"))
  if it, ok := items[id]; ok {
    if it.OwnerID != getCurrentUserID(c) {
      c.JSON(http.StatusForbidden, gin.H{"error": "forbidden"})
      return
    }
    c.JSON(http.StatusOK, gin.H{"id": it.ID, "data": it.Data})
    return
  }
  c.Status(http.StatusNotFound)
}

func main() {
  r := gin.Default()
  r.GET("/items/:id", vulnerable)     // vulnerable
  r.GET("/items-secure/:id", fixed)   // fixed
  r.Run()
}

CVE References

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