VNX-GO-040: Logging of sensitive data
Detects Go logging calls (log, slog, zap, logrus, zerolog, any logger) whose arguments carry a password, token, secret, API key, private key, card number or Authorization header.
| Field | Value |
|---|---|
| Rule ID | VNX-GO-040 |
| Severity | High |
| SARIF level | error |
| Kind | sast |
| Languages | Go |
| CWE | CWE-532 |
| CAPEC | CAPEC-63 |
| ATT&CK | T1059.007 |
| CWSS vector | CWSS:1.0/TI:H/AP:RU/AL:S/IC:N/FC:LT/RP:RU/RL:S/AV:L/AS:N/IN:A/SC:A/BI:H/DI:H/EX:H/EC:N/P:H |
| Tags | logging sensitive-data information-disclosure |
| Source | internal/sast/rules/vnx-go-040.rego |
Overview
This rule flags calls to the standard log package, log/slog, zap, logrus, zerolog, klog/glog and any logger variable whose arguments (not the message text) carry a secret: a variable or field named for a password, passphrase, secret, token, API key, private key, credentials, card number, CVV or SSN, the request’s Authorization/Cookie header, or a structured field keyed by such a name. Values passed through redact, mask, len or a hash, comparisons such as apiKey != "", and lookalike names (nextPageToken, tokenCount, secretName) are not reported. Logging sensitive data exposes it to anyone with access to the log infrastructure. This maps to CWE-532: Insertion of Sensitive Information into Log File.
Sensitive data in logs is a persistent, silent risk: unlike a live API response, log data accumulates in files, is shipped to centralised aggregation platforms, archived to cold storage, and often retained for months or years.
Why This Matters
Log infrastructure is rarely treated with the same security controls as credential storage systems. Log files are often world-readable on servers, replicated to insecure backup destinations, forwarded to cloud SIEM platforms with less granular access control, and visible to support engineers, DevOps personnel, and external contractors. A single log.Printf("user %s authenticated with password %s", user, password) statement can expose that password to dozens of people and systems that have no business need to see it.
Beyond internal exposure, compliance frameworks including PCI-DSS (Requirement 3), HIPAA (§164.312), GDPR (Article 32), and SOC 2 (CC6.1) explicitly prohibit logging authentication credentials, payment card data, and other sensitive personal information. A log file containing passwords or card numbers can constitute a reportable data breach. Attackers who compromise a logging pipeline or SIEM gain access to an aggregated, searchable archive of credentials across all users and time periods, a far more valuable target than a single account compromise.
What Gets Flagged
A logging call whose arguments (not its message text) carry a secret:
// FLAGGED: standard log
log.Printf("login attempt user=%s password=%s", user, password)
log.Fatalf("cannot authenticate %s with %s", req.Username, req.Password)
log.Printf("request auth header: %s", r.Header.Get("Authorization"))
// FLAGGED: structured loggers keyed by a secret name
logger.Info("connecting", zap.String("api_key", apiKey))
slog.Info("login", "user", c.user, "password", pw)
logrus.WithField("token", c.cfg.Token).Info("refreshing")
log.Debug().Str("secret", c.signingSecret).Msg("loaded")
// FLAGGED: a logger reached through a getter or wrapper
c.Logger().Infof("exchanged code for %s", accessToken)
lg.Sugar().Debugf("calling upstream with key %s", apiKey)
// FLAGGED: call broken across lines
logger.Infof("config loaded: dsn=%s token=%s",
cfg.DSN,
cfg.APIToken)
Not flagged: log.Printf("password reset requested for %s", email), ErrInvalidToken, len(password), redact(token), mask(cardNumber), zap.String("password", "[REDACTED]"), "hasAPIKey", apiKey != "", and lookalikes such as nextPageToken, tokenCount, passwordPolicy, secretName, keyID.
Remediation
-
Never log the value of sensitive fields. Log the event (authentication failure, token validation error) with enough context to diagnose problems (username, request ID, error type) but without the secret itself.
// SAFE: structured log with no sensitive value exposed func handleLogin(w http.ResponseWriter, r *http.Request) { username := r.FormValue("username") password := r.FormValue("password") // only used for auth check, never logged if err := auth.Verify(username, password); err != nil { log.Printf("login failed: user=%q reason=%v", username, err) http.Error(w, "invalid credentials", http.StatusUnauthorized) return } log.Printf("login success: user=%q", username) } -
Implement a redacting logger wrapper that scrubs known-sensitive fields before writing log entries. This provides a safety net for cases where sensitive data is accidentally included in a struct that gets logged.
// SAFE: mask sensitive fields in log output func maskSecret(s string) string { if len(s) <= 4 { return "****" } return s[:4] + strings.Repeat("*", len(s)-4) } -
Use structured logging (e.g.
zap,zerolog,slog) with field-level redaction rules. Structured loggers make it easier to audit exactly which fields are logged and to apply redaction at the sink level. -
Scan log output in CI using tools like
truffleHogorgitleaksrun against captured test logs to catch accidental credential logging before it reaches production.
References
- CWE-532: Insertion of Sensitive Information into Log File
- OWASP Logging Cheat Sheet
- OWASP Top 10 A09:2021 – Security Logging and Monitoring Failures
- CAPEC-63: Simple Script Injection
- PCI-DSS Requirement 3 – Protect Stored Account Data
- Go log/slog package documentation
- go.uber.org/zap documentation
Suppressing this rule
Put a nosec comment on the line, or on line 1 to cover the whole file. Use the comment marker of the file’s language (#, //, -- or ;):
# nosec vnx-go-040
// nosec vnx-go-040
Or record an ignore rule: vulnetix ignore add --rule vnx-go-040 --reason "...". See Suppressing findings.