Broken Object Property Level Authorization

Broken Object Property Level Authorization - Go (Gin) [GHSA-69x8-hrgq-fjj8]

[Updated Jun 2026] Updated GHSA-69x8-hrgq-fjj8

Overview

Broken Object Property Level Authorization (BOPLA) flaws allow an attacker to access resources or perform actions belonging to other users when an API exposes an object ID to the client and does not enforce ownership checks. In Go with Gin, endpoints often take a resource ID from the path or query string and return or modify that resource without verifying the caller's rights. This creates indirect object references and can leak data or enable unauthorized updates. Remediation involves enforcing ownership in all access paths: authenticate the caller, fetch the resource with a query that includes the owner or access scope, and reject requests that don't match. Centralized authorization logic or middleware helps ensure consistent checks. Best practices include adopting a model like RBAC/ABAC, never trusting client-provided IDs alone, and adding tests and monitoring to catch leaks or misconfigurations early. Ensure error messages do not reveal sensitive details.

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{
  "1": {ID: "1", OwnerID: "alice", Data: "Alice's secret"},
  "2": {ID: "2", OwnerID: "bob", Data: "Bob's secret"},
}

// Auth middleware (uses header X-User-ID)
func auth() gin.HandlerFunc {
  return func(c *gin.Context) {
    user := c.Request.Header.Get("X-User-ID")
    if user == "" {
      c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "unauthorized"})
      return
    }
    c.Set("userID", user)
    c.Next()
  }
}

// Vulnerable: ownership not checked
func vulnerableGetResource(c *gin.Context) {
  id := c.Param("id")
  res, ok := resources[id]
  if !ok {
    c.JSON(http.StatusNotFound, gin.H{"error": "not found"})
    return
  }
  c.JSON(http.StatusOK, res)
}

// Fixed: ownership checked
func fixedGetResource(c *gin.Context) {
  id := c.Param("id")
  res, ok := resources[id]
  if !ok {
    c.JSON(http.StatusNotFound, gin.H{"error": "not found"})
    return
  }
  user, _ := c.Get("userID")
  if userID, ok := user.(string); !ok || res.OwnerID != userID {
    c.JSON(http.StatusForbidden, gin.H{"error": "forbidden"})
    return
  }
  c.JSON(http.StatusOK, res)
}

func main() {
  r := gin.Default()
  r.Use(auth())
  // Vulnerable endpoint
  r.GET("/resources/:id", vulnerableGetResource)
  // Fixed endpoint
  r.GET("/secure/resources/:id", fixedGetResource)
  r.Run(":8080")
}

CVE References

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