PII Leak in Response (Go)
ID |
pii_leak_in_response_go |
Severity |
high / critical (composite with |
Remediation Complexity |
medium |
Remediation Risk |
high |
Remediation Effort |
medium |
Family |
API3:2023 - Broken Object Property Level Authorization |
CWE |
CWE-359 |
Resource |
data_exposure |
Language |
net/http, Gin, Echo, Chi, Fiber |
Description
Reports endpoints whose response model carries fields tagged PII (Personally Identifiable Information), PCI (payment-card data), or PHI (protected health information) by the sensitivity classifier. Distinct from excessive_data_exposure in scope:
-
excessive_data_exposure— over-fetching, any sensitivity-tagged field the client didn’t ask for (including credentials). -
pii_leak_in_response— sensitive-data sinks. Any time PII / PCI / PHI reaches the wire, regardless of whether the client asked for it.
CREDENTIAL and CRYPTO_MATERIAL tags are intentionally excluded — login and OAuth endpoints legitimately return tokens / cookies, and treating those as PII leaks would drown the signal.
The detector pairs with unauthenticated_endpoint via the FlawCorrelator: when the same endpoint also has no authentication requirement, a composite CRITICAL pii_leak_in_unauthenticated_endpoint finding is emitted.
Rationale
PII / PCI / PHI in a response is not automatically a defect — a user fetching their own profile needs to see their email and phone. The risk is when the response carries those fields beyond the operation’s intended scope:
-
Cross-user leak —
/users/search?name=alicereturns full PII for every match. The endpoint’s purpose is "find a username"; returning email / phone / DOB / address is over-disclosure. -
Anonymous / partner endpoint leak — a B2B partner endpoint that returns a list of customers with full PII (or worse, PCI / PHI), where the partner’s contract entitles them to aggregate numbers only.
-
Unauthenticated leak — the worst case. Any unauthenticated PII / PCI / PHI sink is a critical breach — under GDPR Art-32 ("appropriate technical measures") and PCI-DSS 3.4 ("render PAN unreadable"), there’s no defensible "well, it’s TLS-encrypted" argument.
The regulatory side compounds the technical one: a single unauthenticated PII endpoint discovered by a journalist or competitor is a public disclosure event. Treat PII / PCI / PHI in responses as a default-deny shape and justify each exposure case by case.
A susceptible Gin / GORM search handler:
type User struct {
ID int64
Username string
Email string // PII
Phone string // PII
DateOfBirth string // PII
Address string // PII
}
func SearchUsers(c *gin.Context) {
var users []User
db.Where("username LIKE ?", "%"+c.Query("q")+"%").Find(&users)
c.JSON(http.StatusOK, users)
}
Find(&users) populates the full struct; c.JSON marshals every exported field — including the PII columns.
Remediation
Three approaches, in order of robustness:
-
Remove the field from the response DTO if the operation doesn’t genuinely require it. The fastest fix; defaults to least disclosure.
-
Mask or redact at the response boundary. Show
j***@example.cominstead ofjohn.doe@example.com, last-four-digits of a phone number, the first letter of a name. Pair with full unmasked access from a separate, audited "details" endpoint. -
Tighten authorization on the endpoint. If the field is necessary, confirm the caller is entitled to it — pair this finding with a BOLA / BFLA review. An admin reading any user’s email is acceptable; a logged-in non-admin reading any user’s email is not.
Cross-cutting:
-
If the sensitivity tag is wrong for your project — e.g., an email-newsletter app where
emailis intentionally public — override the tag rather than suppressing the finding. -
Pair this detector with
unauthenticated_endpointreview. The composite CRITICAL finding is the actionable one.
A revised handler with an explicit summary struct:
type UserSummary struct {
ID int64 `json:"id"`
Username string `json:"username"`
}
func SearchUsers(c *gin.Context) {
var summaries []UserSummary
db.Model(&User{}).
Select("id", "username").
Where("username LIKE ?", "%"+c.Query("q")+"%").
Find(&summaries)
c.JSON(http.StatusOK, summaries)
}
GORM’s Select("id", "username") mirrors the wire shape on the database side; PII columns never leave the table.
Configuration
The detector accepts minConfidence (default high) and inherits the sensitivity classifier’s global configuration. The set of tags considered "PII" (PII / PCI / PHI) is fixed; CREDENTIAL and CRYPTO_MATERIAL are intentionally excluded from this detector’s scope.
References
-
OWASP API Security Top 10 (2023) - API3:2023 - Broken Object Property Level Authorization.
-
OWASP API Security Top 10 (2023) - API10:2023 - Unsafe Consumption of APIs.
-
CWE-359 : Exposure of Private Personal Information to an Unauthorized Actor.
-
OWASP Cheat Sheets Series: User Privacy Protection Cheat Sheet.
-
GDPR Article 32 — Security of processing.
-
PCI-DSS v4.0 — Requirement 3.4 (render PAN unreadable wherever it is stored, processed, or transmitted).