Overview
Broken Object Property Level Authorization (BOLLA) vulnerabilities occur when an API returns object data or its properties without verifying that the requesting user is allowed access. The CVE-2026-34839 case in Glances demonstrates how a REST API exposed via '/api/4/*' with no authentication and a permissive CORS policy (Access-Control-Allow-Origin: *) can enable cross-origin data exfiltration. While Glances is not written in Go, the underlying risk - information disclosure when authorization checks are skipped and CORS is misconfigured - is directly applicable to Go (Gin) APIs. In a Go/Gin context, attackers can access sensitive fields of a resource (for example, secret or owner-only data) simply by requesting a resource ID that they do not own, especially if you return full structs or use unfiltered DTOs. This mirrors CWE-200 (Information Exposure) and CWE-306 (Missing Authentication for Critical Data), and can be amplified by misconfigured CORS (CWE-942).
Affected Versions
Glances: prior to 4.5.4 (vulnerable); fixed in 4.5.4.
Code Fix Example
Go (Gin) API Security Remediation
Vulnerable pattern (Go + Gin):
package main
import (
"net/http"
"github.com/gin-gonic/gin"
"github.com/gin-contrib/cors"
)
type Resource struct {
ID int64 `json:"id"`
OwnerID int64 `json:"owner_id"`
Data string `json:"data"`
Secret string `json:"secret"`
}
var store = map[string]Resource{
"1": {ID: 1, OwnerID: 1001, Data: "public data", Secret: "topsecret"},
"2": {ID: 2, OwnerID: 1002, Data: "more data", Secret: "verysecret"},
}
func fetchResource(id string) Resource {
if r, ok := store[id]; ok {
return r
}
return Resource{ID: 0, OwnerID: 0, Data: "", Secret: ""}
}
func vulnResource(c *gin.Context) {
id := c.Param("id")
res := fetchResource(id) // no auth check, returns entire object including Secret
c.JSON(http.StatusOK, res)
}
func main() {
r := gin.Default()
// Vulnerable CORS: allows any origin
vuln := r.Group("/vuln")
vuln.Use(func(c *gin.Context) {
c.Writer.Header().Set("Access-Control-Allow-Origin", "*")
if c.Request.Method == http.MethodOptions {
c.AbortWithStatus(http.StatusNoContent)
return
}
c.Next()
})
vuln.GET("/resource/:id", vulnResource)
// Fixed path: proper auth, per-object authorization, and restricted CORS
secure := r.Group("/secure")
secure.Use(func(c *gin.Context) {
// Simple example auth (replace with real JWT/OAuth in prod)
userID := c.GetHeader("Authorization")
if userID == "" {
c.AbortWithStatus(http.StatusUnauthorized)
return
}
c.Set("userID", userID)
c.Next()
})
secure.Use(cors.New(cors.Config{
AllowOrigins: []string{"https://trust.example.com"},
AllowMethods: []string{"GET"},
AllowHeaders: []string{"Origin", "Content-Type", "Authorization"},
AllowCredentials: true,
}))
secure.GET("/resource/:id", func(c *gin.Context) {
// Extract user from header (example only)
user := c.GetHeader("Authorization")
// In a real app, decode token and extract user id
id := c.Param("id")
res := fetchResource(id)
// Ownership check (per-object authorization)
if user != "" && res.OwnerID != 0 {
// Simulated userID from header matches OwnerID (for demo)
// In production: compare against parsed token userID
// if int64(parsedUserID) != res.OwnerID { c.AbortWithStatus(http.StatusForbidden) ; return }
_ = user
// Here we enforce ownership: only allow if the requester owns the resource
// For demonstration, assume ownership verified when header equals OwnerID string
if user != string(rune(res.OwnerID)) {
c.AbortWithStatus(http.StatusForbidden)
return
}
}
// Safe response: do not leak secrets
safe := gin.H{
"id": res.ID,
"data": res.Data,
}
c.JSON(http.StatusOK, safe)
})
r.Run(":8080")
}
Vulnerable pattern (side-by-side):
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
type Resource struct { ID int64 `json:"id"`; OwnerID int64 `json:"owner_id"`; Data string `json:"data"`; Secret string `json:"secret"` }
var store = map[string]Resource{
"1": {ID: 1, OwnerID: 1001, Data: "public data", Secret: "topsecret"},
}
func vulnResource(c *gin.Context) { res := store[c.Param("id")]; c.JSON(http.StatusOK, res) }
func main() {
r := gin.Default()
r.GET("/vuln/resource/:id", vulnResource)
r.Run(":8081")
}