Overview
Broken Function Level Authorization (BFLA) allows attackers to perform actions that should be limited to specific functions or roles by manipulating requests instead of relying on per-function checks. In web APIs, this often means endpoints that are accessible to any authenticated user, or that interpret the action from request data rather than enforcing explicit access controls for each function. The consequence can be admin-like operations, data exfiltration, or destructive actions performed under a normal user's session, especially in multi-tenant or SaaS environments with shared resources.
In Go with Gin, code tends to authorize once at the route or to implement a single switch that maps a requested action to a function. If the handler only confirms authentication and then uses a parameter (action, id, or function name) to decide what to execute, attackers can call restricted operations by altering the parameter. The vulnerability often resides in missing per-action or per-endpoint checks, insufficient binding of permissions to routes, or centralized function dispatch that doesn't enforce the correct actor or role for the requested operation.
Remediation patterns include enforcing explicit authorization checks for each endpoint or action, centralizing policy decisions, and avoiding parameter-based dispatch for privileged operations. Implement per-user role checks in middleware or per-route guards, validate resource ownership, and use signed tokens or claims to decide permission. Add unit/integration tests that simulate attempts to perform restricted actions with low-privilege users and verify proper denial.
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 int\n Name string\n Role string\n}\n\nfunc authMiddleware() gin.HandlerFunc {\n return func(c *gin.Context) {\n token := c.GetHeader(\"Authorization\")\n var u User\n switch token {\n case \"token-admin\":\n u = User{ID:1, Name:\"Alice\", Role:\"admin\"}\n case \"token-user\":\n u = User{ID:2, Name:\"Bob\", Role:\"user\"}\n default:\n c.AbortWithStatus(http.StatusUnauthorized)\n return\n }\n c.Set(\"user\", u)\n c.Next()\n }\n}\n\n// Vulnerable handler: no per-action authorization\nfunc vulnerableAdminAction(c *gin.Context) {\n action := c.Param(\"action\")\n switch action {\n case \"deleteUser\":\n c.JSON(http.StatusOK, gin.H{\"status\": \"user deleted (vulnerable)\"})\n case \"promoteUser\":\n c.JSON(http.StatusOK, gin.H{\"status\": \"user promoted (vulnerable)\"})\n default:\n c.Status(http.StatusNotFound)\n }\n}\n\nfunc fixedAdminAction(c *gin.Context) {\n // enforce per-action authorization\n v, ok := c.Get(\"user\")\n if !ok {\n c.AbortWithStatus(http.StatusUnauthorized)\n return\n }\n user := v.(User)\n if user.Role != \"admin\" {\n c.AbortWithStatus(http.StatusForbidden)\n return\n }\n\n action := c.Param(\"action\")\n switch action {\n case \"deleteUser\":\n c.JSON(http.StatusOK, gin.H{\"status\": \"user deleted (fixed)\"})\n case \"promoteUser\":\n c.JSON(http.StatusOK, gin.H{\"status\": \"user promoted (fixed)\"})\n default:\n c.Status(http.StatusNotFound)\n }\n}\n\nfunc main() {\n r := gin.Default()\n r.Use(authMiddleware())\n\n // Vulnerable path\n r.POST(\"/admin/vuln/:action\", vulnerableAdminAction)\n\n // Fixed path\n r.POST(\"/admin/fix/:action\", fixedAdminAction)\n\n r.Run(\":8080\")\n}\n