Information Disclosure in Gin
How Information Disclosure Manifests in Gin
In Gin, information disclosure often occurs when handlers return more data than necessary or expose internal diagnostics through error responses. A common pattern is returning a full model struct directly with c.JSON.
type User struct {
ID int `json:"id"`
Username string `json:"username"`
Password string `json:"password"` // should never be sent
SSN string `json:"ssn"` // sensitive personal data
}
func GetUser(c *gin.Context) {
u := User{ID: 1, Username: "alice", Password: "secret123", SSN: "123-45-6789"}
c.JSON(http.StatusOK, u) // leaks Password and SSN
}
Another vector is verbose error messages. Gin’s default recovery middleware returns a stack trace when the application runs in debug mode:
r := gin.Default() // includes Logger & Recovery
r.GET("/panic", func(c *gin.Context) {
panic("something went wrong") // recovery will show file/line in response
})
If gin.SetMode(gin.DebugMode) is left enabled in production, the recovery middleware writes the stack trace to the response body, potentially revealing file paths, variable values, or configuration details. Additionally, binding JSON with c.ShouldBindJSON without struct validation can allow attackers to inject extra fields that later get reflected in responses or logs, leading to indirect disclosure.
These patterns map to OWASP API Security Top 10 2023 – API4: Excessive Data Exposure and API8: Security Misconfiguration.
Gin-Specific Detection
Detecting these issues requires observing what the API actually returns and what error information it exposes. middleBrick’s unauthenticated black‑box scan includes a Data Exposure check that inspects JSON payloads for field names commonly associated with secrets (e.g., password, ssn, api_key) and looks for stack‑trace‑like strings in error responses.
You can start a scan from the CLI:
middlebrick scan https://api.example.com/users
The tool will:
- Issue a GET request to the target endpoint.
- Parse the response body and flag any sensitive field names.
- Check for strings matching common traceback patterns (e.g.,
at main.main,runtime,file:) that indicate a leaked stack trace. - Report findings with severity, location, and remediation guidance.
In a CI/CD pipeline, the GitHub Action can be configured to fail a build if the score drops below a threshold:
name: API Security
on: [push]
jobs:
scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Run middleBrick
uses: middlebrick/action@v1
with:
api-url: https://staging.example.com
fail-below: B # fail if score is B or lower
The action posts a JSON summary to the workflow log, making it easy to track trends over time via the middleBrick Dashboard.
Gin-Specific Remediation
Fixing information disclosure in Gin relies on limiting what is serialized and suppressing diagnostic output in production.
1. Return only needed fields
Define a dedicated response struct or use gin.H with explicit keys:
type UserResponse struct {
ID int `json:"id"`
Username string `json:"username"`
}
func GetUser(c *gin.Context) {
u := User{ID: 1, Username: "alice", Password: "secret", SSN: "123-45-6789"}
resp := UserResponse{ID: u.ID, Username: u.Username}
c.JSON(http.StatusOK, resp)
}
Alternatively, use struct tags to omit sensitive fields:
type User struct {
ID int `json:"id"`
Username string `json:"username"`
Password string `json:"- "` // omitted from JSON
SSN string `json:"- "`
}
func GetUser(c *gin.Context) {
u := User{ID: 1, Username: "alice", Password: "secret", SSN: "123-45-6789"}
c.JSON(http.StatusOK, u) // Password and SSN are not serialized
}
2. Hide stack traces and internal errors
Set Gin to release mode before creating the router:
gin.SetMode(gin.ReleaseMode) // disables verbose debug output r := gin.New()Then customize the recovery middleware to return a generic message:
r.Use(gin.CustomRecovery(func(c *gin.Context, err any) { c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{ "error": "internal server error", }) }))3. Validate and sanitize input binding
Use a validation library to ensure only expected fields are accepted and reject unknown keys:
type CreateUserReq struct { Username string `json:"username" binding:"required,alphanum"` Email string `json:"email" binding:"required,email"` } func CreateUser(c *gin.Context) { var req CreateUserReq if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "invalid request"}) return } // proceed with safe data c.JSON(http.StatusCreated, gin.H{"message": "user created"}) }By combining explicit response structs, release‑mode configuration, and customized error handling, you eliminate the most common Gin‑specific information disclosure paths while preserving API functionality.
Frequently Asked Questions
Does middleBrick modify my Gin API to fix information disclosure?
Can I rely on Gin’s default recovery middleware in production?
gin.SetMode(gin.ReleaseMode)) or replace the recovery handler with a version that returns generic error messages to avoid leaking internal details.