HIGH bola idorginbearer tokens

Bola Idor in Gin with Bearer Tokens

Bola Idor in Gin with Bearer Tokens — how this specific combination creates or exposes the vulnerability

Broken Object Level Authorization (BOLA) occurs when an API exposes an endpoint that accepts user-supplied identifiers and returns or modifies data without verifying that the requesting identity is authorized for that specific resource. In a Gin-based service that uses Bearer Tokens for authentication, this commonly arises when an endpoint like /users/:userID/profile or /accounts/:accountID only checks that a valid Bearer Token exists, but does not confirm that the subject (sub) or other claims within the token refer to the requested :userID or :accountID. Because the authentication check is decoupled from the authorization check, an attacker can copy a valid Bearer Token issued to another user and iterate over predictable resource IDs to access or manipulate data across accounts.

Endpoints that combine Bearer Token validation with numeric or sequential IDs are especially prone to BOLA. For example, a token may contain sub: "usr_1001", but the handler reads param.Arg("userID") directly and uses it in a database query without comparing it to the token’s subject. MiddleBrick’s scan would flag this as a BOLA/IDOR finding because the unauthenticated attack surface allows enumeration of valid IDs and unauthorized data access. The presence of Bearer Tokens does not inherently prevent BOLA; it is the lack of a per-request ownership or scope check that creates the vulnerability.

In Gin, this often maps to the OWASP API Top 10 API1:2023-BROKEN_OBJECT_LEVEL_AUTHORIZATION and can intersect with insecure direct object references (IDOR). If the API also exposes related endpoints such as /accounts/:accountID/transactions or /organizations/:orgID/members, and none of them validate that the token’s claims align with the path parameter, an attacker can traverse relationships and access data outside their scope. Even with Bearer Tokens, without scoping each request to the token’s permissions and resource ownership, the API remains vulnerable to unauthorized read and write operations.

Bearer Tokens-Specific Remediation in Gin — concrete code fixes

To fix BOLA in Gin when using Bearer Tokens, enforce that every request validates not only the token’s authenticity but also that the resource being accessed belongs to the token’s subject or authorized scopes. Below are concrete, working examples that demonstrate a secure pattern.

Example 1: Validate token subject against path parameter

Parse the Bearer Token, extract the subject claim, and compare it to the ID in the route before querying the datastore.

package main

import (
    "net/http"
    "strings"

    "github.com/gin-gonic/gin"
    "github.com/golang-jwt/jwt/v5"
)

type Claims struct {
    Sub string `json:"sub"`
    jwt.RegisteredClaims
}

func authMiddleware() gin.HandlerFunc {
    return func(c *gin.Context) {
        auth := c.GetHeader("Authorization")
        if auth == "" {
            c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "authorization header required"})
            return
        }
        parts := strings.Split(auth, " ")
        if len(parts) != 2 || parts[0] != "Bearer" {
            c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "invalid authorization header format"})
            return
        }
        tokenStr := parts[1]
        claims := &Claims{}
        token, err := jwt.ParseWithClaims(tokenStr, claims, func(token *jwt.Token) (interface{}, error) {
            // TODO: provide your key function, e.g., RSA public key or HMAC secret
            return []byte("your-secret-key"), nil
        })
        if err != nil || !token.Valid {
            c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "invalid token"})
            return
        }
        c.Set("claims", claims)
        c.Next()
    }
}

func GetProfile(c *gin.Context) {
    claims, _ := c.Get("claims")
    tokenSub := claims.(*Claims).Sub

    userID := c.Param("userID")
    if tokenSub != userID {
        c.AbortWithStatusJSON(http.StatusForbidden, gin.H{"error": "you can only access your own profile"})
        return
    }

    // Proceed to fetch profile for userID knowing tokenSub == userID
    c.JSON(http.StatusOK, gin.H{"profile_for": userID})
}

Example 2: Scope-based authorization with roles and accounts

For endpoints that involve an account or organization ID, compare token claims to ensure the token grants access to that specific account.

func AccountMiddleware() gin.HandlerFunc {
    return func(c *gin.Context) {
        auth := c.GetHeader("Authorization")
        if auth == "" {
            c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "authorization header required"})
            return
        }
        parts := strings.Split(auth, " ")
        if len(parts) != 2 || parts[0] != "Bearer" {
            c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "invalid authorization header format"})
            return
        }
        tokenStr := parts[1]
        claims := &Claims{}
        token, err := jwt.ParseWithClaims(tokenStr, claims, func(token *jwt.Token) (interface{}, error) {
            return []byte("your-secret-key"), nil
        })
        if err != nil || !token.Valid {
            c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "invalid token"})
            return
        }
        c.Set("claims", claims)
        c.Next()
    }
}

type AccountClaims struct {
    Sub    string   `json:"sub"`
    Orgs   []string `json:"orgs"`
    Scopes []string `json:"scopes"`
    jwt.RegisteredClaims
}

func AccessAccount(c *gin.Context) {
    claims, _ := c.Get("claims")
    tokenOrgs := claims.(*AccountClaims).Orgs

    accountID := c.Param("accountID")
    hasAccess := false
    for _, org := range tokenOrgs {
        if org == accountID {
            hasAccess = true
            break
        }
    }
    if !hasAccess {
        c.AbortWithStatusJSON(http.StatusForbidden, gin.H{"error": "token does not grant access to this account"})
        return
    }

    // Proceed to handle accountID safely with token-based scope verification
    c.JSON(http.StatusOK, gin.H{"access_to_account": accountID})
}

Additional recommendations include using short-lived tokens with refresh mechanisms, enforcing scopes that map to endpoints, and auditing logs to detect enumeration attempts. MiddleBrick can validate these patterns by scanning your Gin endpoints and confirming that authorization checks are tied to the token context, reducing the likelihood of BOLA/IDOR.

Related CWEs: bolaAuthorization

CWE IDNameSeverity
CWE-250Execution with Unnecessary Privileges HIGH
CWE-639Insecure Direct Object Reference CRITICAL
CWE-732Incorrect Permission Assignment HIGH

Frequently Asked Questions

Does using Bearer Tokens in Gin automatically prevent BOLA?
No. Bearer Tokens authenticate requests but do not enforce object-level permissions. Without explicit checks that the token’s subject or scopes match the requested resource ID, BOLA/IDOR can still occur.
Can MiddleBrick detect BOLA in endpoints that use Bearer Tokens?
Yes. MiddleBrick scans the unauthenticated attack surface and can detect missing ownership and scope checks that enable BOLA/IDOR, even when Bearer Tokens are required for authentication.