Broken Function Level Authorization

Broken Function Level Authorization in Go (Gin) [GHSA-7429-hxcv-268m]

[Updated April 2026] Updated GHSA-7429-hxcv-268m

Overview

Broken Function Level Authorization (BFLA) occurs when an API grants access to high-level functions rather than enforcing access at the resource level. In Go with Gin, this often happens when authorization checks are performed against the operation (e.g., read, write, delete) at the route level or within a common handler, without validating that the authenticated user is permitted to act on the specific resource identified in the request. Attackers with broad function permissions can therefore access, modify, or delete resources they should not be able to touch, especially when ownership or resource-specific ACLs are ignored. This class of vulnerability can lead to data leakage, data tampering, or unauthorized administrative actions across users and tenants in multi-tenant or multi-user systems.

Code Fix Example

Go (Gin) API Security Remediation
// Vulnerable and fixed example (Go with Gin)
package main

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

type User struct {
  ID          int
  Role        string
  Permissions map[string]bool
}

type ResourceACL struct {
  OwnerID int
  Allowed map[int]bool
}

var resourceACLs = map[string]ResourceACL{
  "doc1": {OwnerID: 1, Allowed: map[int]bool{2: true}},
  "doc2": {OwnerID: 2, Allowed: map[int]bool{3: true}},
}

func main() {
  r := gin.Default()

  // Mock auth middleware
  r.Use(func(c *gin.Context) {
    c.Set("user", &User{ID: 2, Role: "user", Permissions: map[string]bool{"read": true, "delete": true}})
    c.Next()
  })

  // Vulnerable pattern: function-level authorization without resource context
  r.GET("/vuln/docs/:id", func(c *gin.Context) {
    u := c.MustGet("user").(*User)
    if u == nil || !u.Permissions["read"] {
      c.AbortWithStatus(http.StatusForbidden)
      return
    }
    id := c.Param("id")
    c.JSON(http.StatusOK, gin.H{"doc": id, "owner": resourceACLs[id].OwnerID})
  })

  // Fixed pattern: per-resource authorization
  r.GET("/docs/:id", func(c *gin.Context) {
    u := c.MustGet("user").(*User)
    id := c.Param("id")
    if !hasAccess(u, id, "read") {
      c.AbortWithStatus(http.StatusForbidden)
      return
    }
    c.JSON(http.StatusOK, gin.H{"doc": id, "owner": resourceACLs[id].OwnerID})
  })

  r.Run(":8080")
}

// hasAccess implements per-resource access control
func hasAccess(u *User, resourceID string, operation string) bool {
  acl, ok := resourceACLs[resourceID]
  if !ok {
    return false
  }
  if u.ID == acl.OwnerID {
    return true
  }
  if allowed, ok := acl.Allowed[u.ID]; ok && allowed {
    return true
  }
  if u != nil && u.Role == "admin" {
    return true
  }
  return false
}

CVE References

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