Overview
Broken Object Level Authorization (BOLA) in Go Gin can allow an attacker to access or manipulate another user’s data by simply guessing or enumerating object identifiers in ID-based routes. In real-world deployments, APIs often expose endpoints like /resources/:id or /orders/:orderId without proper ownership checks, enabling data leakage, privacy violations, and regulatory risk when access is granted solely by the presence of an ID. This class of vulnerability is especially dangerous in multi-tenant apps, financial apps, and healthcare services where resources are strictly scoped to owners. Without robust authorization, even authenticated users can read, modify, or delete data they should not control, undermining trust and exposing sensitive information.
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 db = map[int64]Resource{1: {ID: 1, OwnerID: 42, Data: "secret"}}
func getResource(id int64) (Resource, bool) {
r, ok := db[id]
return r, ok
}
// Vulnerable: no ownership check
func vulnerableHandler(c *gin.Context) {
idStr := c.Params[0].Value
id, err := strconv.ParseInt(idStr, 10, 64)
if err != nil {
c.Status(http.StatusBadRequest)
return
}
res, ok := getResource(id)
if !ok {
c.Status(http.StatusNotFound)
return
}
c.JSON(http.StatusOK, res)
}
// Fixed: ownership check
func fixedHandler(c *gin.Context) {
idStr := c.Params[0].Value
id, err := strconv.ParseInt(idStr, 10, 64)
if err != nil {
c.Status(http.StatusBadRequest)
return
}
res, ok := getResource(id)
if !ok {
c.Status(http.StatusNotFound)
return
}
uidVal, exists := c.Get("userID")
if !exists {
c.Status(http.StatusUnauthorized)
return
}
uid := uidVal.(int64)
if res.OwnerID != uid {
c.Status(http.StatusForbidden)
return
}
c.JSON(http.StatusOK, res)
}
func main() {
r := gin.Default()
r.GET("/resources/:id", vulnerableHandler)
r.GET("/resources-fixed/:id", fixedHandler)
r.Run()
}