4.7 Review Information Disclosure and Logging
4.7 - Review Information Disclosure and Logging
Overview
Disclosure findings leak secrets or decision logic through messages, URLs, comments, or logs. Ask what a stranger learns from each channel and whether sensitive fields are redacted before they leave the trust boundary.
The points below are the ideas this family chapter uses again and again.
- Disclosure findings leak secrets, PII, or internals through errors, URLs, logs, or comments.
- Enumeration and verbose errors teach attackers what exists.
- Logging is both a control and a sink for sensitive data.
- Related variants share one question: what should never leave the trust boundary.
- Evidence names the channel, the sensitive value, missing control, impact, and a proving test.
After reading this chapter, we should be able to find disclosure paths in errors, URLs, enumeration, logging, and comments with shared evidence habits.
Shared Review Model
Across every variant below, keep the same evidence habit: name the source, the sink, the missing control, the impact, and a test that would prove a fix.
Variants in This Family
Error page disclosure
Information disclosure through errors happens when the application answers a failure with more detail than the user should see. Default servlet containers, reverse proxies, and frameworks often ship verbose error pages that name server software and version. Application code may print exceptions directly to the response writer or include SQL fragments, file paths, and library versions in JSON error bodies.
Where to look
| Signal | Where to look |
|---|---|
| Feature type | API error JSON, catch blocks, global exception handlers, health endpoints on failure |
| Response sinks | printStackTrace, traceback.format_exc(), returning ex.Message or ex.StackTrace |
| Debug flags | DEBUG=True, app.debug, developer exception page enabled in production deploy paths |
| Missing error pages | No catch-all /error route; container defaults expose version banners |
| Partial handlers | 404/500 pages configured without general fallback hiding stack traces |
| Version banners | Server, X-Powered-By, framework version strings in production configs |
Appendix detail: Error page disclosure code reference.
Sensitive data in URL
Sensitive data exposure via URLs occurs when passwords, tokens, session identifiers, or personal data appear in the query string or path where GET semantics apply. Browsers store URLs in history. Load balancers and CDNs often log full request lines. Third-party sites may receive secrets when users follow external links from a page that included them in the URL.
Where to look
| Signal | Where to look |
|---|---|
| Feature type | Login, registration, password reset, magic links, API key auth, OAuth callbacks, shareable report URLs |
| Input entry | GET query params, bookmarkable links, email links with embedded tokens, front-end URL builders |
| Sensitive fields | Passwords, API keys, access tokens, session IDs, reset tokens, PII in query strings |
| Logging sinks | Access logs, APM, error trackers, analytics beacons that capture full request URIs |
| Weak controls | @app.route(..., methods=["GET", "POST"]) on login, [FromQuery] password, redirect URLs echoing credentials |
| Referrer leakage | Pages with secrets in the URL linked from external sites without Referrer-Policy |
Appendix detail: Sensitive data in URL code reference.
Username enumeration
Username enumeration is an information disclosure issue in authentication and account recovery. The application behaves differently when a username or email is registered versus when it is not. Attackers compile lists of valid accounts for credential stuffing, phishing, or targeted attacks.
Where to look
| Signal | Where to look |
|---|---|
| Feature type | Login, password reset, registration, invite acceptance, MFA enrollment, OAuth account linking |
| Input entry | Email or username fields on public-facing auth endpoints |
| Branching logic | if user is None, if token != null, different HTTP status or JSON error codes |
| Side channels | Response time when DB lookup is skipped, email send only when account exists |
| Weak controls | 404 for unknown email, explicit "already registered", distinct login failure messages |
| API leaks | JSON fields like exists: true, different error codes per case |
Appendix detail: Username enumeration code reference.
Sensitive logging
Sensitive logging is the practice of writing data to logs that should remain confidential. If logs are copied to analytics, emailed on alert, or accessed by operators with broad read rights, a single log.info("password=" + password) can cause a breach as serious as database exposure.
Where to look
| Signal | Where to look |
|---|---|
| Feature type | Login, payment, password reset, OAuth token exchange, API key validation, crypto operations |
| Log sinks | Application loggers, access logs, APM traces, exception handlers, stdout in containers |
| Sensitive fields | Passwords, session IDs, bearer tokens, API keys, PAN/CVV, reset tokens, connection strings |
| Weak controls | Interpolating request.json, full header dumps, debug logging on auth paths in production |
| Missing positives | No audit trail for login failure, lockout, or admin actions without storing secrets |
| Shipping risk | Log aggregation to Splunk, CloudWatch, or ELK without redaction filters |
Appendix detail: Sensitive logging code reference.
Secure logging
Logs are a secondary data store. They often have weaker access controls than production databases, longer retention, and broad read access for operations teams. When applications log secrets, session identifiers, or personal data, a log breach may cause the same harm as a database leak. Missing security events—failed logins, authorization denials, admin actions—blind incident response and forensics.
Where to look
| Signal | Where to look |
|---|---|
| Feature type | Login, logout, password reset, payment, admin config, file upload handlers |
| Secret leakage | Logged passwords, API keys, Authorization headers, cookies, connection strings |
| Missing audit events | Auth flows without success/failure logging, absent logout and password-change records |
| PII and PCI | Full card numbers, bank accounts, government IDs, health data in log fields |
| Oververbose debug | logger.debug(request) serializing entire HTTP requests in production paths |
| Log injection | User input embedded in log messages without sanitization |
| Admin activity gaps | Privileged actions without immutable audit trail |
Appendix detail: Secure logging code reference.
Sensitive code comments
Source code comments are not executed, but they are often stored in version control, included in builds, and visible to anyone with repository access. When comments contain passwords, password hints, API keys, or instructions to weaken security, they create an information disclosure risk. Attackers who obtain source may use those notes to bypass authentication or reach hidden functionality.
Where to look
| Signal | Where to look |
|---|---|
| Comment types | Block comments, HTML <!-- -->, Javadoc, docstrings, TODO/FIXME/HACK markers |
| Credential language | password, secret, key, token, example values that look real |
| Bypass hints | Instructions to skip MFA, disable validation, or use a backdoor account |
| Internal topology | Internal hostnames, database names, unreleased feature flags in comments |
| HTML exposure | JSP, Thymeleaf, static HTML comments visible in page source |
| Commented-out code | Disabled auth checks and hardcoded overrides left for debugging |
Appendix detail: Sensitive code comments code reference.
Worked Example (Error page disclosure)
We walk Error page disclosure in depth. Apply the same tracing steps to the other variants, adjusting sources and sinks from the tables above.
Sample vulnerable code (Python)
import traceback
import logging
import uuid
from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse
app = FastAPI()
logger = logging.getLogger("reports")
@app.get("/reports/run")
async def run_report(request: Request):
try:
return await generate_report()
except Exception as exc:
# Stack trace returned to client — reveals paths, ORM queries, and library versions
return JSONResponse(
{"error": str(exc), "trace": traceback.format_exc()},
status_code=500,
)
Step-by-step review walkthrough
- Locate every catch block and exception handler that writes to HTTP responses or API error payloads.
- Trace the FastAPI report route. In the sample,
traceback.format_exc()in JSON responses exposes internal paths and ORM details. - Check framework and server configuration.
web.xmlerror pages, Springserver.error.*, DjangoDEBUG, FastAPI exception handlers, and Express error middleware. - Trace whether stack traces reach clients in any environment via
printStackTrace,e.message, or full exception serialization. - Review API layers that serialize exceptions into JSON (
detail,stack, embedded file paths). - Confirm a catch-all error page exists so undefined HTTP error codes do not fall back to container defaults.
- Inspect health and diagnostic endpoints that echo environment variables or build metadata on failure.
- Verify logging sends stack traces to secure server logs, not to the user agent.
Risk Impact (Family)
Targeted exploitation. Stack traces reveal framework versions, file paths, and SQL fragments attackers use to craft precise exploits.
Architecture mapping. Error details expose internal module names, dependency versions, and deployment layout.
Credential and query leakage. Database exceptions may include connection hints, table names, or parameter values.
Compliance exposure. Verbose errors conflict with policies requiring minimal client-facing diagnostic data.
Support confusion. Raw exceptions shown to end users erode trust and complicate incident triage.
Fix Principles
Primary safer patterns for the worked example follow. For other languages and variant-specific fixes, use the family appendix.
Python
Register error handlers that return generic messages. Log full exceptions server-side with a correlation ID.
import traceback
import logging
import uuid
from fastapi import FastAPI
from fastapi.responses import JSONResponse
app = FastAPI()
logger = logging.getLogger("reports")
@app.exception_handler(Exception)
async def handle_error(request, exc):
request_id = str(uuid.uuid4())
logger.exception("unhandled error request_id=%s", request_id)
return JSONResponse(
{"error": "An internal error occurred.", "request_id": request_id},
status_code=500,
)
@app.get("/reports/run")
async def run_report():
return await generate_report()
Important: Keep Django DEBUG=False in production. Use handler500 and logging settings for details. Never return traceback.format_exc() to users.
Verify During Review
Use the checklist below as a family-level pass over the variants above. Each item should map to evidence in the change under review.
- No stack traces, SQL errors, or file paths appear in HTTP responses in production configurations.
- Catch-all and per-status error pages are configured so container defaults never leak versions.
- Framework debug modes and detailed error middleware are disabled outside local development.
- APIs return stable, minimal error shapes; support staff use correlation IDs tied to server logs.
- Exception logging is complete server-side but excludes secrets already covered in logging review.
- Security headers and custom error content are defense in depth, not the only control.
- Passwords, API keys, and long-lived tokens never appear in GET query strings or shareable URLs.
- Login and registration use POST with secrets in body or
Authorizationheader over HTTPS. - Access logs, APM, and error reporting do not store full URLs for authentication endpoints.
- Redirects after sensitive operations strip credentials from the visible URL bar.
Referrer-Policyand cache headers are set where pages might still touch sensitive flows.- OpenAPI or public docs do not advertise secret-bearing query parameters.
- Login, reset, and registration responses do not differ in wording, status, or structure based on account existence.
- Email and SMS are triggered only server-side; clients cannot infer delivery from response alone.
- Timing and workload are similar enough that trivial timing attacks are not trivially enabled.
- Rate limits and monitoring protect public authentication endpoints.
- Intentional enumeration (admin consoles) is role-gated and documented.
- API documentation matches uniform external behavior.
- Passwords, session IDs, access tokens, API keys, and connection strings never appear in application or access logs.
- Security-relevant events (login, logout, reset attempts, authz failures, admin actions) are logged with useful non-secret context.
- Debug logging of full requests is disabled in production or heavily redacted.
- Log shipping and retention policies treat log buckets as sensitive data stores.
- Developers removed temporary debug prints of tokens from reset and OAuth flows.
- Exception messages returned to users are separate from rich detail allowed only in server-side logs.
- All login attempts — successful and unsuccessful
- Log-outs
- Password changes and reset attempts
- User creation and removal, and changes to a user's authorization
- Authorization failures when a user is denied access to a resource
- Input validation failures, such as unexpected values from dropdown lists
- System administration activity
- Integrity events and submission of user-generated content — especially file uploads
- Access to sensitive data such as payment card information and keys
- Application source code and commercially sensitive information
- Session IDs, access tokens, and authentication passwords
- Sensitive personal data, bank account, or payment cardholder data
- Database connection strings, encryption keys, and other secrets
- Information that is illegal to collect or that the user has opted out of collecting
- Production log level defaults to INFO or WARN; DEBUG and TRACE do not emit PII in steady state.
- Error responses to users remain generic while server-side logs capture detail without secrets.
- Log aggregation storage has encryption, retention limits, and role-based access aligned with compliance needs.
- No passwords, API keys, tokens, or bypass instructions appear in comments, HTML, or commit messages in the repository.
- TODO and FIXME items on security work are tracked in issue trackers with restricted access, not as inline hints.
- Commented-out authentication, authorization, or validation code is removed rather than left as a re-enable target.
- Static secret scanning runs on every push and blocks merges when comments contain high-risk patterns.
- Production page source does not expose sensitive HTML comments to unauthenticated users.
Code Reference (Appendix)
Payloads, language-specific sinks, multi-language examples, and full fix catalogs for every variant live in 4.7 code reference — Review Information Disclosure and Logging.
Reference
-
Appendix — 4.7 code reference
-
CWE-209: Generation of Error Message Containing Sensitive Information
- CWE-497: Exposure of Sensitive System Information
- OWASP Error Handling Cheat Sheet
- RFC 7807: Problem Details for HTTP APIs
- Django — Error reporting
- Flask — Error handling
- Spring Boot — Error handling properties
- ASP.NET Core — Handle errors
- Go log/slog package
- CWE-598: Use of GET Request Method With Sensitive Query Strings
- OWASP — Information exposure through query strings in URL
- MDN — Referrer-Policy
- Flask — HTTP methods
- FastAPI — Header parameters
- Spring Security — Form Login
- ASP.NET Core — Prevent cross-site request forgery
- Go net/http — Request handling
- CWE-204: Observable Response Discrepancy
- OWASP — Testing for Account Enumeration
- OWASP Authentication Cheat Sheet
- Django — Password reset views
- ASP.NET Core Identity — UserManager
- Spring Security — Authentication failure handling
- bcrypt — Python documentation
- CWE-532: Insertion of Sensitive Information into Log File
- OWASP — Logging Cheat Sheet
- Python logging — Filters
- SLF4J — Structured logging
- Serilog — Destructure policies
- Go slog package
- ASP.NET Core — Logging
- CWE-117: Improper Output Neutralization for Logs
- OWASP Logging Cheat Sheet
- OWASP Application Security Verification Standard
- Python logging documentation
- structlog documentation
- SLF4J manual
- Serilog destructuring
- Go zap logger
- CWE-615: Inclusion of Sensitive Information in Source Code Comments
- OWASP — Secrets Management Cheat Sheet
- Python os.environ
- dotnet user-secrets
- Azure Key Vault configuration provider
- HashiCorp Vault
- Gitleaks