Broken Object Level Authorization

Broken Object Level Authorization in Go (Gin) [May 2026] [CVE-2025-66170]

[Updated May 2026] Updated CVE-2025-66170

Overview

CVE-2025-66170 demonstrates a Broken Object Level Authorization (BOLA) flaw in which an authenticated user could enumerate backups across accounts when the API exposed an object listing endpoint without enforcing per-object ownership. In the affected CloudStack versions 4.21.0.0 and 4.22.0.0, a user with valid credentials could access the backups API and list backups from other accounts, exposing account-scoped metadata even though the contents of the backups remained protected. The CVE is categorized under CWE-863 and highlights the risk of object-level access control failures in multi-tenant environments. The issue was fixed in version 4.22.0.1, which closes the authorization gap and prevents cross-account enumeration. In the Go (Gin) ecosystem, BOLA manifests when a handler accepts a client-provided object identifier or filter (for example, account_id) and uses it to query a list or detail without validating that the authenticated user owns or is permitted to access those objects. This leads to leakage of metadata (e.g., backup IDs, names) across accounts. Attackers can leverage such gaps to map resources, plan targeted abuse, or mount further attacks even if they cannot view the actual backup contents. The remediation is to enforce strict per-object authorization in the service layer, primarily by scoping queries to the authenticated user’s account and validating ownership for any resource access by ID. This guide provides Go (Gin) patterns and a concrete code example showing both the vulnerable pattern and a corrected, secure version. The fix demonstrates enforcing account scoping at the data-access layer and ensuring that object IDs cannot be used to bypass ownership checks.

Affected Versions

4.21.0.0 - 4.22.0.0

Code Fix Example

Go (Gin) API Security Remediation
package main

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

type User struct { ID string; AccountID string }
type Backup struct { ID string; AccountID string; Name string }

var allBackups = []Backup{
  {ID: "b1", AccountID: "A1", Name: "backup-1"},
  {ID: "b2", AccountID: "A2", Name: "backup-2"},
  {ID: "b3", AccountID: "A1", Name: "backup-3"},
}

func main() {
  r := gin.Default()
  r.GET("/vuln/backups", vulnerableListBackups)
  r.GET("/fixed/backups", fixedListBackups)
  r.Run()
}

// Mock auth: pretend to extract user from context
func getUserFromContext(c *gin.Context) struct{ ID, AccountID string } {
  return struct{ ID, AccountID string }{ID: "u1", AccountID: "A1"}
}

// Vulnerable: client can override account_id to enumerate backups
func vulnerableListBackups(c *gin.Context) {
  _ = getUserFromContext(c)
  accountID := c.Query("account_id")
  var res []Backup
  for _, b := range allBackups {
    if b.AccountID == accountID {
      res = append(res, b)
    }
  }
  c.JSON(http.StatusOK, res)
}

// Fixed: enforce ownership by filtering by user’s account
func fixedListBackups(c *gin.Context) {
  user := getUserFromContext(c)
  var res []Backup
  for _, b := range allBackups {
    if b.AccountID == user.AccountID {
      res = append(res, b)
    }
  }
  c.JSON(http.StatusOK, res)
}

CVE References

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