Broken Object Property Level Authorization

Broken Object Property Level Authorization in Go (Gin) [CVE-2025-13997]

[Updated March 2026] Updated CVE-2025-13997

Overview

Broken Object Property Level Authorization (BOPLA) vulnerabilities occur when an API returns object properties that an attacker should not be allowed to see. While many examples focus on full-resource access, BOPLA extends to partial exposure where sensitive fields such as keys, tokens, or internal metadata are included in responses. The CVE-2025-13997 case demonstrates the broader risk pattern: an unauthenticated service exposed API keys by embedding them into HTML/response content in a WordPress plugin, highlighting how easily secrets can leak when strict property-level controls are absent. In Go with Gin, the risk manifests when a handler marshals internal models directly or uses shared DTOs that include sensitive fields. If the caller is not authorized to see a particular property but the API negotiates or filters only at the resource level rather than the property level, an attacker could infer or access the extra properties via crafted requests. Remediation is to enforce per-property access checks, isolate response shapes via DTOs, and avoid serializing internal fields. For code remediation, implement explicit response types that whitelist fields, apply an authorization function per property, and consider role-based or policy-based checks. Also include tests and code-analysis to enforce property-level redaction.

Code Fix Example

Go (Gin) API Security Remediation
/* Vulnerable pattern */
package main

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

type User struct {
  ID int `json:"id"`
  Email string `json:"email"`
  Secret string `json:"secret"`
  Admin bool `json:"admin"`
}

func main() {
  r := gin.Default()
  // Vulnerable: serializes internal Secret field to client
  r.GET("/vuln/user/:id", func(c *gin.Context) {
    user := User{ID: 1, Email: "[email protected]", Secret: "apikey-ABC-123", Admin: false}
    c.JSON(http.StatusOK, user)
  })

  // Fixed: use a DTO that excludes sensitive fields
  type UserPublic struct {
    ID int `json:"id"`
    Email string `json:"email"`
  }
  r.GET("/fix/user/:id", func(c *gin.Context) {
    user := User{ID: 1, Email: "[email protected]", Secret: "apikey-ABC-123", Admin: false}
    resp := UserPublic{ID: user.ID, Email: user.Email}
    c.JSON(http.StatusOK, resp)
  })

  r.Run()
}

CVE References

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