Broken Object Level Authorization

Broken Object Level Authorization in Go (Gin) [CVE-2026-33428]

[Updated March 2026] Updated CVE-2026-33428

Overview

Broken Object Level Authorization (BOLA) vulnerabilities occur when an API fails to enforce per-object access controls, allowing users to access resources they shouldn’t. CVE-2026-33428 illustrates this in Discourse: a non-staff user with elevated group membership could access deleted posts belonging to any user due to an overly broad authorization check on the deleted posts index endpoint. In Go applications using Gin, a similar pattern can manifest when a list (index) endpoint gates access with a general permission and then returns all matching resources (e.g., all deleted posts) regardless of ownership or specific object-level permissions. Such oversight can enable attackers to enumerate or retrieve data from other users simply by possessing a higher-level role or group, undermining data confidentiality and trust boundaries. This guide ties the concept to Go (Gin) implementations and provides concrete remediation patterns.

Affected Versions

Prior to 2026.3.0-latest.1, 2026.2.1, and 2026.1.2; patched in 2026.3.0-latest.1, 2026.2.1, and 2026.1.2.

Code Fix Example

Go (Gin) API Security Remediation
Vulnerable:
package main

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

type User struct {
    ID     int64
    IsStaff bool
    Groups []string
}

type Post struct {
    ID       int64
    OwnerID  int64
    Deleted  bool
}

var posts = []Post{
    {ID: 1, OwnerID: 1, Deleted: true},
    {ID: 2, OwnerID: 2, Deleted: true},
    {ID: 3, OwnerID: 1, Deleted: false},
}

func getUserFromContext(c *gin.Context) User { // mock authentication
    return User{ID: 1, IsStaff: false, Groups: []string{"elevated"}}
}

func contains(slice []string, s string) bool {
    for _, v := range slice {
        if v == s { return true }
    }
    return false
}

// Vulnerable: authorization check is broad and returns all deleted posts if user is staff OR in elevated group
func getDeletedPostsVulnerable(c *gin.Context) {
    user := getUserFromContext(c)
    if user.IsStaff || contains(user.Groups, "elevated") {
        // Returns all deleted posts regardless of owner
        var res []Post
        for _, p := range posts {
            if p.Deleted {
                res = append(res, p)
            }
        }
        c.JSON(http.StatusOK, res)
        return
    }
    c.Status(http.StatusForbidden)
}

// Fixed: enforce per-object authorization (only admins see all; others see only their own deleted posts)
func getDeletedPostsFixed(c *gin.Context) {
    user := getUserFromContext(c)
    if user.IsStaff {
        var res []Post
        for _, p := range posts {
            if p.Deleted {
                res = append(res, p)
            }
        }
        c.JSON(http.StatusOK, res)
        return
    }
    var own []Post
    for _, p := range posts {
        if p.Deleted && p.OwnerID == user.ID {
            own = append(own, p)
        }
    }
    c.JSON(http.StatusOK, own)
}

func main() {
    r := gin.Default()
    r.GET("/deleted-posts/vulnerable", getDeletedPostsVulnerable)
    r.GET("/deleted-posts/fixed", getDeletedPostsFixed)
    r.Run()
}

CVE References

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