Broken Object Level Authorization

Broken Object Level Authorization in Go (Gin) [Mar 2026] [GHSA-g2pf-ww5m-2r9m]

[Updated Mar 2026] Updated GHSA-g2pf-ww5m-2r9m

Overview

Broken Object Level Authorization (BOLA) occurs when an API exposes an object via an identifier and relies solely on authentication to gate access, allowing a user to access resources owned by others. In Go applications using the Gin framework, handlers that fetch a resource by ID and return it based only on a path parameter can inadvertently reveal data belonging to different users. Attackers can enumerate IDs or craft requests to retrieve or modify assets they should not see, resulting in data leakage, privacy violations, and potential regulatory exposure. The real-world impact ranges from private user data exposure (profiles, documents, records) to improper state changes if write operations bypass proper access checks. When BOLA affects sensitive objects, an attacker may perform read or write operations across user-owned resources, escalate privileges, or cause integrity issues. This is particularly risky in stateless APIs where JWTs or sessions authenticate the user but authorization checks are not consistently enforced against each resource. In Gin-based services, BOLA commonly manifests as queries that filter by resource ID alone, or as authorization logic that only runs after fetching data. Such patterns enable access to resources outside the requester’s permission scope. Root causes include missing ownership checks in handlers, insufficient data-layer access control, and fragmented authorization logic spread across middleware and business logic without a single source of truth. Mitigation involves enforcing authorization consistently for every data access path. Treat resources as owned by a principal (owner_id) and require that all reads and writes include ownership or policy checks. Propagate the authenticated user identity via middleware, centralize authorization decisions, and use parameterized queries that constrain both id and owner_id (or apply a robust RBAC/ABAC policy). Regularly test BOLA scenarios and audit access logs to detect unauthorized access attempts.

Code Fix Example

Go (Gin) API Security Remediation
// Vulnerable pattern: fetch by id without ownership check
package main

import (
  "database/sql"
  "log"
  "net/http"
  "os"
  "github.com/gin-gonic/gin"
  _ "github.com/go-sql-driver/mysql"
)

type Resource struct {
  ID      int64
  OwnerID int64
  Data    string
}

var db *sql.DB

func main() {
  var err error
  // DSN comes from environment for safety in demonstration
  dsn := os.Getenv("DSN")
  db, err = sql.Open("mysql", dsn)
  if err != nil {
    log.Fatal(err)
  }
  defer db.Close()

  r := gin.Default()
  r.GET("/vuln/resources/:id", vulnerableGetResource)
  r.GET("/resources/:id", fixedGetResource)
  r.Run(":8080")
}

// Vulnerable: does not verify ownership
func vulnerableGetResource(c *gin.Context) {
  id := c.Param(`id`)
  var res Resource
  err := db.QueryRow(`SELECT id, owner_id, data FROM resources WHERE id = ?`, id).Scan(&res.ID, &res.OwnerID, &res.Data)
  if err != nil {
    c.JSON(http.StatusNotFound, gin.H{`error`: `resource not found`})
    return
  }
  c.JSON(http.StatusOK, res)
}

// Fixed: enforce ownership in the query
func fixedGetResource(c *gin.Context) {
  id := c.Param(`id`)
  v, ok := c.Get(`user_id`)
  if !ok {
    c.JSON(http.StatusUnauthorized, gin.H{`error`: `unauthorized`})
    return
  }
  userID, _ := v.(int64)
  var res Resource
  err := db.QueryRow(`SELECT id, owner_id, data FROM resources WHERE id = ? AND owner_id = ?`, id, userID).Scan(&res.ID, &res.OwnerID, &res.Data)
  if err != nil {
    c.JSON(http.StatusNotFound, gin.H{`error`: `resource not found or access denied`})
    return
  }
  c.JSON(http.StatusOK, res)
}

// Fixed version is exposed via /resources/:id; vulnerable version is /vuln/resources/:id

CVE References

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