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
}