Broken Object Property Level Authorization

Broken Object Property Level Authorization in Go (Gin) [GHSA-68m9-983m-f3v5]

[Updated Apr 2026] Updated GHSA-68m9-983m-f3v5

Overview

Broken Object Property Level Authorization (BOLOA) vulnerabilities occur when a server returns data tied to an object ID without validating that the requester has rights to access that object. In Go applications using the Gin framework, this often happens when handlers extract an ID from the URL and fetch a resource directly, bypassing ownership checks. Attackers can enumerate IDs and retrieve data belonging to other users, leading to data leakage and privacy violations. Real-world impact includes exposure of personal information, documents, messages, or financial data, and it can enable further abuse such as fraud or manipulation if write endpoints are affected. Without proper checks, an attacker can perform unauthorized reads or writes simply by altering the object identifier in the request. In the Go (Gin) context, the vulnerability typically manifests as: using c.Param(\"id\") to locate a resource, returning it as JSON, or performing updates without verifying that the authenticated user owns or is allowed to modify that resource. CVEs are not listed for this guide; no CVEs provided (N/A). Remediation focuses on enforcing object-level authorization in a centralized way, validating ownership in the business/service layer, and applying a deny-by-default policy. Implement authentication, attach user identity to requests, check that resource.OwnerID matches the authenticated user, and adopt RBAC/ABAC. Add tests and monitoring to prevent regressions.

Code Fix Example

Go (Gin) API Security Remediation
package main

import (
  "net/http"
  "strconv"

  "github.com/gin-gonic/gin"
)

type Resource struct {
  ID      int64
  OwnerID int64
  Data    string
}

var resources = map[int64]Resource{
  1: {ID:1, OwnerID:101, Data:"Alice's notes"},
  2: {ID:2, OwnerID:202, Data:"Bob's notes"},
}

func main() {
  r := gin.Default()

  // Vulnerable: no authorization check on ownership
  r.GET("/vuln/resource/:id", func(c *gin.Context) {
    id, err := strconv.ParseInt(c.Param("id"), 10, 64)
    if err != nil {
      c.JSON(http.StatusBadRequest, gin.H{\"error\":\"invalid id\"})
      return
    }
    if res, ok := resources[id]; ok {
      c.JSON(http.StatusOK, res)
      return
    }
    c.JSON(http.StatusNotFound, gin.H{\"error\":\"not found\"})
  })

  // Fixed: enforce explicit ownership check with auth
  r.GET("/fixed/resource/:id", AuthMiddleware(), func(c *gin.Context) {
    id, err := strconv.ParseInt(c.Param("id"), 10, 64)
    if err != nil {
      c.JSON(http.StatusBadRequest, gin.H{\"error\":\"invalid id\"})
      return
    }
    res, ok := resources[id]
    if !ok {
      c.JSON(http.StatusNotFound, gin.H{\"error\":\"not found\"})
      return
    }
    if cur, exists := c.Get("currentUserID"); exists {
      if cur.(int64) != res.OwnerID {
        c.JSON(http.StatusForbidden, gin.H{\"error\":\"forbidden\"})
        return
      }
    } else {
      c.JSON(http.StatusUnauthorized, gin.H{\"error\":\"unauthorized\"})
      return
    }
    c.JSON(http.StatusOK, res)
  })

  r.Run(":8080")
}

func AuthMiddleware() gin.HandlerFunc {
  return func(c *gin.Context) {
    uidStr := c.GetHeader("X-User-ID")
    uid, err := strconv.ParseInt(uidStr, 10, 64)
    if err != nil {
      c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{\"error\":\"unauthorized\"})
      return
    }
    c.Set("currentUserID", uid)
    c.Next()
  }
}

CVE References

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