Overview
Broken Object Property Level Authorization in Go (Gin) is a common source of data leakage when handlers fetch resources by id without validating that the requester owns or is permitted to access the resource. In real-world apps, attackers can enumerate IDs or manipulate parameters to access unrelated user data, leading to horizontal privilege escalation and privacy violations. When such flaws exist in APIs serving sensitive data, the impact ranges from unauthorized data exposure to potential compromise of multi-tenant boundaries, with downstream consequences for compliance and user trust.
Code Fix Example
Go (Gin) API Security Remediation
// Vulnerable and fixed endpoints are exposed side-by-side in a single Gin app for demonstration.
package main
import (
"net/http"
"strconv"
"github.com/gin-gonic/gin"
)
type Widget struct {
ID int64
OwnerID int64
Data string
}
var widgets = map[int64]Widget{
1: {ID: 1, OwnerID: 10, Data: "secret A"},
2: {ID: 2, OwnerID: 20, Data: "secret B"},
}
func main() {
r := gin.Default()
// Vulnerable: no per-object authorization
r.GET("/widgets/:id", vulnerable)
// Fixed: authorization checks that the requester owns the object
r.GET("/widgets-fixed/:id", fixed)
r.Run(":8080")
}
func vulnerable(c *gin.Context) {
id, _ := strconv.ParseInt(c.Param("id"), 10, 64)
if w, ok := widgets[id]; ok {
c.JSON(http.StatusOK, w)
return
}
c.Status(http.StatusNotFound)
}
func fixed(c *gin.Context) {
id, _ := strconv.ParseInt(c.Param("id"), 10, 64)
w, ok := widgets[id]
if !ok {
c.Status(http.StatusNotFound)
return
}
// Simple user identity from header for demonstration
userID := getUserID(c)
if w.OwnerID != userID {
c.JSON(http.StatusForbidden, map[string]string{"error": "forbidden"})
return
}
c.JSON(http.StatusOK, w)
}
func getUserID(c *gin.Context) int64 {
s := c.GetHeader("X-User-ID")
id, _ := strconv.ParseInt(s, 10, 64)
return id
}