Broken Object Property Level Authorization

Broken Object Property Level Authorization in Go (Gin) [GHSA-37j4-88rp-2f6h]

[Updated May 2026] Updated GHSA-37j4-88rp-2f6h

Overview

Broken Object Property Level Authorization (BOPLA) vulnerabilities occur when an API returns object data without verifying that the requester is allowed to access each object's properties. In Gin-based Go services, endpoints often fetch a resource by ID from the URL and serialize the entire object, inadvertently leaking ownership or sensitive fields to unauthorized users. In real-world apps, this can lead to exposure of other users' data, restricted attributes such as private identifiers, or even modification of properties by manipulating IDs if server-side checks are inconsistent. This class of vulnerability is common when endpoints rely solely on authentication and coarse authorization, without explicit granularity to guard individual properties. In Go with the Gin framework, the issue typically manifests when handlers fetch a model by ID and return it unchanged, or when the ORM query does not constrain on the current user's ownership. The fix is to enforce per-request ownership checks, shape responses to a limited view, and, whenever possible, gate property access behind policy decisions.

Code Fix Example

Go (Gin) API Security Remediation
package main

import (
  "net/http"
  "github.com/gin-gonic/gin"
  "gorm.io/gorm"
)

type Order struct {
  ID           string `gorm:\"primaryKey\"`
  UserID       string
  Amount       float64
  CreditCard   string
}

// Vulnerable pattern: returns the full resource without verifying ownership
func vulnerableGetOrder(db *gorm.DB) gin.HandlerFunc {
  return func(c *gin.Context) {
    orderID := c.Param("orderId")
    var order Order
    if err := db.First(&order, "id = ?", orderID).Error; err != nil {
      c.JSON(http.StatusNotFound, gin.H{\"error\": \"not found\"})
      return
    }
    c.JSON(http.StatusOK, order)
  }
}

// Fixed pattern: enforce ownership and project safe fields
func safeGetOrder(db *gorm.DB) gin.HandlerFunc {
  return func(c *gin.Context) {
    userID, ok := c.Get("userID")
    if !ok {
      c.JSON(http.StatusUnauthorized, gin.H{\"error\": \"unauthorized\"})
      return
    }
    orderID := c.Param("orderId")
    var order Order
    if err := db.First(&order, "id = ? AND user_id = ?", orderID, userID).Error; err != nil {
      c.JSON(http.StatusNotFound, gin.H{\"error\": \"not found\"})
      return
    }
    // Project to a DTO to exclude sensitive fields
    c.JSON(http.StatusOK, gin.H{
      \"id\": order.ID,
      \"amount\": order.Amount,
    })
  }
}

func main() {
  // setup gin, db, routes omitted for brevity
}

CVE References

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