Broken Function Level Authorization

Broken Function Level Authorization in Go (Gin) [March 2026] [GHSA-p77j-4mvh-x3m3]

[March 2026] Updated GHSA-p77j-4mvh-x3m3

Overview

Broken Function Level Authorization (BFLA) occurs when an API function is gated by a role or a broad permission check rather than a per-resource policy. In Gin-based Go services, this vulnerability often arises when middleware or handler logic only verifies that a user is authenticated or has a high-level role, and then allows operations on any resource without confirming that the user is permitted to act on that specific item. No CVEs are provided for this guide. Real-world impact includes unauthorized reads, updates, or deletions across tenants or users. Attackers may enumerate resource IDs via path parameters or request bodies and perform actions on resources they should not own. The consequence can be data leakage, data integrity issues, regulatory risk, and loss of customer trust. Root causes in Go (Gin) implementations include relying on route-level checks or roles instead of per-resource validation, or performing ownership checks after the action is attempted; failing to validate the target resource before applying changes; and leveraging middleware that does not attach enough context for per-resource decisions. Mitigation involves enforcing resource-level authorization in the request path, using checks like res.OwnerID == user.ID or scoped policies; centralizing authorization into middleware or dedicated helpers; and adding tests that cover multi-tenant and cross-resource access scenarios.

Code Fix Example

Go (Gin) API Security Remediation
package main\n\nimport (\n  \"net/http\"\n  \"github.com/gin-gonic/gin\"\n)\n\ntype User struct {\n  ID string\n  Role string\n}\n\ntype Resource struct {\n  ID string\n  OwnerID string\n  Data string\n}\n\nvar resources = map[string]Resource{}\n\nfunc init() {\n  resources[\"1\"] = Resource{ID: \"1\", OwnerID: \"u1\", Data: \"secret1\"}\n  resources[\"2\"] = Resource{ID: \"2\", OwnerID: \"u2\", Data: \"secret2\"}\n}\n\nfunc authMiddleware() gin.HandlerFunc {\n  return func(c *gin.Context) {\n    uid := c.GetHeader(\"X-User-ID\")\n    role := c.GetHeader(\"X-User-Role\")\n    if uid == \"\" {\n      c.AbortWithStatus(http.StatusUnauthorized)\n      return\n    }\n    c.Set(\"user\", User{ID: uid, Role: role})\n    c.Next()\n  }\n}\n\nfunc updateResourceVulnerable(c *gin.Context) {\n  u, _ := c.Get(\"user\")\n  user := u.(User)\n  resID := c.Param(\"id\")\n  res, ok := resources[resID]\n  if !ok {\n    c.AbortWithStatus(http.StatusNotFound)\n    return\n  }\n  // Vulnerable: function-level authorization without per-resource check\n  if user.Role != \"admin\" && user.Role != \"editor\" {\n    c.AbortWithStatus(http.StatusForbidden)\n    return\n  }\n  newData := c.Query(\"data\")\n  res.Data = newData\n  resources[resID] = res\n  c.JSON(http.StatusOK, res)\n}\n\nfunc updateResourceFixed(c *gin.Context) {\n  u, _ := c.Get(\"user\")\n  user := u.(User)\n  resID := c.Param(\"id\")\n  res, ok := resources[resID]\n  if !ok {\n    c.AbortWithStatus(http.StatusNotFound)\n    return\n  }\n  // Fixed: per-resource authorization\n  if res.OwnerID != user.ID && user.Role != \"admin\" {\n    c.AbortWithStatus(http.StatusForbidden)\n    return\n  }\n  newData := c.Query(\"data\")\n  res.Data = newData\n  resources[resID] = res\n  c.JSON(http.StatusOK, res)\n}\n\nfunc main() {\n  r := gin.Default()\n  r.Use(authMiddleware())\n  r.PATCH(\"/resources/:id\", updateResourceVulnerable)\n  r.PATCH(\"/resources/:id/fix\", updateResourceFixed)\n  r.Run(\":8080\")\n}\n

CVE References

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