Broken Object Property Level Authorization

Broken Object Property Level Authorization in Go (Gin) [GHSA-m48g-4wr2-j2h6]

[Updated Mar 2026] Updated GHSA-m48g-4wr2-j2h6

Overview

Broken Object Property Level Authorization (also called per-resource access control failures) occurs when an API allows a user to read, modify, or delete properties of resources that they do not own or are not permitted to access. In real-world deployments this can lead to data leakage, privacy violations, and privilege escalation across tenants or user accounts. Attackers may enumerate resource IDs or manipulate request parameters to access other users' data, even when authentication succeeds. Without proper object-level checks, authorization becomes effectively global for a user, amplifying risk across logging, analytics, and regulatory compliance. When CVEs are not provided for a given implementation, this class of vulnerability remains a foundational risk to address proactively in both development and security testing. In Go with Gin, this vulnerability often manifests when an endpoint reads a resource ID from the URL (for example, /resources/:id), fetches the resource, and returns it without verifying that the authenticated user owns the resource or has explicit access rights. If the authentication middleware only sets a user identity but does not enforce resource-level authorization, an attacker can request resources owned by others by altering the ID. This is particularly dangerous if responses include sensitive fields or if write paths do not enforce ownership strictly. The root cause is typically missing or inconsistent authorization checks at the service or data access layer, not the absence of authentication. Concretely, you’ll commonly see patterns where the code trusts client-provided IDs and returns data after a simple fetch, or where ownership data is only hinted at in the client payload. In Gin-based services, mixing per-request validation with loosely coupled data access can lead to replay or ID-guessing attacks. To remediate, enforce explicit ownership verification in all code paths that touch resource-specific data, and centralize authorization logic to avoid drifting checks across handlers. Use policy-driven checks, restrict what is returned in responses, and add tests that simulate owner vs non-owner requests to confirm correct denial of access. Remediation involves strict per-resource authorization, centralized policies, and comprehensive testing. Ensure authentication establishes a verifiable user identity, then validate that the resource’s OwnerID (or equivalent access control attribute) matches the authenticated user before returning data. Prefer queries that enforce ownership at the data layer, return 403 Forbidden for unauthorized access, and minimize exposure of sensitive fields. Regularly audit code paths that access resource identifiers and maintain RBAC/ABAC alignment with business rules.

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
}

// Vulnerable: returns resource without ownership check
func vulnerableHandler(c *gin.Context) {
  userID := c.GetString("userID")
  resID := c.Param("id")
  res := fetchResource(resID)
  if res == nil {
    c.Status(http.StatusNotFound)
    return
  }
  // No ownership verification
  c.String(http.StatusOK, "%s", res.Data)
}

// Fixed: enforce ownership before returning resource
func secureHandler(c *gin.Context) {
  userID := c.GetString("userID")
  resID := c.Param("id")
  res := fetchResource(resID)
  if res == nil {
    c.Status(http.StatusNotFound)
    return
  }
  if res.OwnerID != userID {
    c.Status(http.StatusForbidden)
    return
  }
  c.String(http.StatusOK, "%s", res.Data)
}

func main() {
  r := gin.Default()
  // mock middleware setting userID
  r.Use(func(c *gin.Context) {
    c.Set("userID", "user-123")
    c.Next()
  })
  r.GET("/resources/:id", vulnerableHandler)        // vulnerable
  r.GET("/secure/resources/:id", secureHandler)     // fixed
  r.Run(":8080")
}

func fetchResource(id string) *Resource {
  // In a real app, this would query the DB with ownership in mind
  // Here, OwnerID is purposely different from the mocked user to illustrate the flaw
  return &Resource{ID: id, OwnerID: "owner-456", Data: "secret"}
}

CVE References

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