Broken Object Property Level Authorization

Broken Object Property Level Authorization in Go (Gin) [GHSA-95mq-xwj4-r47p]

[Updated Apr 2026] Updated GHSA-95mq-xwj4-r47p

Overview

Broken Object Property Level Authorization vulnerabilities in Go (Gin) can allow attackers to access or modify resources that belong to other users by manipulating identifiers in requests. In multi-tenant systems this can leak highly sensitive data, enable data tampering, and escalate privileges if per-object checks are missing. In Go (Gin), endpoints often take an object id from the path (for example, /resources/:id) and fetch the resource without verifying whether the authenticated user owns or is allowed to access that object. If the authorization check is performed only on authentication (verifying the user is logged in) and not on the resource, an attacker can enumerate IDs to access unauthorized resources. Common patterns include GET/PUT/PATCH on resource endpoints or actions that rely on object IDs in the request. The vulnerability is easy to miss when code follows common patterns but lacks per-object checks, leaving sensitive data exposed. Remediation involves enforcing per-object authorization: verify that resource.OwnerID matches the authenticated user or that the user has an appropriate admin/privileged role, preferably at the DB query level as well. Centralize authorization checks, adopt policy-based controls if possible, and add tests to prevent regressions.

Code Fix Example

Go (Gin) API Security Remediation
package main

import (
    "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: "secret1"},
    "2": {ID: "2", OwnerID: "bob", Data: "secret2"},
}

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

    // Simulated authentication middleware; in real apps, verify JWT or session
    r.Use(func(c *gin.Context) {
        c.Set("userID", "alice")
        c.Next()
    })

    // Vulnerable: returns resource by ID without per-object authorization
    r.GET("/vuln/resources/:id", func(c *gin.Context) {
        id := c.Param("id")
        if res, ok := resources[id]; ok {
            c.JSON(200, res)
            return
        }
        c.JSON(404, gin.H{"error": "not found"})
    })

    // Fixed: enforce per-object authorization by checking ownership
    r.GET("/fixed/resources/:id", func(c *gin.Context) {
        id := c.Param("id")
        userID, _ := c.Get("userID")
        if res, ok := resources[id]; ok {
            if res.OwnerID != userID {
                c.JSON(403, gin.H{"error": "forbidden"})
                return
            }
            c.JSON(200, res)
            return
        }
        c.JSON(404, gin.H{"error": "not found"})
    })

    r.Run(":8080")
}

CVE References

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