Information Leakage (Sensitive Information Exposure)
Also known as: Information disclosure, Sensitive data exposure, Verbose error messages, Banner leakage
A weakness in which an application reveals internal detail, such as stack traces, version banners, debug endpoints or secrets, to people who should never see it, giving an attacker a map of the system.
How it works
Information leakage is the unintended exposure of internal detail to someone who has no need for it. The detail is rarely a breach on its own. A stack trace, a framework version or an internal file path does not steal data, but it tells an attacker which components you run, how they are laid out and where they are weak.
Typical sources are easy to find in a review. Unhandled errors that return the full exception and stack trace to the browser. A debug or diagnostics endpoint left enabled in production. Response headers such as Server or X-Powered-By that name the exact technology and version. Directory listings that show every file in a folder. Comments, source maps or client bundles that carry internal hostnames, identifiers or credentials.
The principle behind the fix is separation. Detail belongs in server-side logs where your team can read it, tied to a correlation id. The user sees a short, generic message and that id. Production configuration turns debug features off, strips technology banners, disables listings and keeps secrets out of anything that is sent to a client.
Because the weakness is configuration and habit rather than one bad line, it is caught by review and by repeatable checks: scan responses for stack traces, probe for debug routes and diagnostic paths, and diff headers across environments. Leaked detail also amplifies other flaws, which is why it sits under security misconfiguration.
Walk through it
- 1Review the error handler
- 2Spot the technology banner
- 3Find the forgotten debug route
- Return a generic error, log the detail
- Harden and verify the fix
A pre-release review flags how the app reports failures. Read the global error handler as a reviewer and note what it sends back to the person who triggered the error.
1// Global error handler (as shipped)2app.use((err, req, res, next) => {3 res.status(500).json({4 error: err.message,5 stack: err.stack,6 query: err.sql,7 node: process.version,8 });9});Spot it
- Responses to users that contain stack traces, exception class names, file paths, SQL text or framework error pages.
- Technology and version banners in Server, X-Powered-By or similar headers.
- Debug, diagnostics or documentation routes reachable in production, such as environment or configuration dumps.
- Directory listings, backup files, source maps or version-control folders served from the web root.
- Internal hostnames, account identifiers or credentials present in client-side code, comments or API responses.
Application access log
ts=2026-10-11T10:02:11Z method=GET path=/tickets/abc status=500 resp_bytes=4380 resp_contains=stack_trace
ts=2026-10-11T10:02:44Z method=GET path=/_debug/env status=200 user=anonymousScanner report
finding=server-banner host=tickets.contoso.example header='X-Powered-By: Express 4.17.1' severity=low
finding=debug-endpoint host=tickets.contoso.example path=/_debug/env severity=highsplSplunk: server errors whose response body leaks detail
index=app sourcetype=access status>=500 resp_contains IN (stack_trace, sql_text, file_path)
| stats count by host, path, statusRequires response inspection or an app-side flag. Treat any hit in production as a finding.
grepgrep: debug routes reached in production logs
grep -E 'path=/(_debug|debug|actuator|phpinfo)' access.log | grep 'status=200'Stop it
Show users a generic error and log the detail server-side
Catch errors centrally, write the full exception with a correlation id to your logs, and return only a short message and that id to the caller. The user needs a reference, not a stack trace.
Turn debug features off and strip banners in production
Disable debug modes, diagnostics routes and verbose frameworks settings in production builds. Remove or genericise Server and X-Powered-By headers, and disable directory listing.
Keep secrets and internal detail out of responses and client code
Review API responses for fields the client does not need, return only what each caller is entitled to, and keep secrets, internal hostnames and identifiers out of bundles, comments and source maps.
Generic error response with server-side logging
Vulnerable
app.use((err, req, res, next) => {
res.status(500).json({ error: err.message, stack: err.stack, query: err.sql });
});Hardened
app.disable("x-powered-by");
app.use((err, req, res, next) => {
const id = crypto.randomUUID();
logger.error({ id, err, path: req.path });
res.status(500).json({ error: "Something went wrong.", reference: id });
});Support staff use the reference to find the full detail in the logs; the caller never sees it.
Response after the fix
Hardened
HTTP/1.1 500 Internal Server Error
Content-Type: application/json
{"error":"Something went wrong.","reference":"5b1f0c2e"}- Run production with debug and verbose error modes disabled, and test that setting in the deployment pipeline.
- Remove or genericise Server and X-Powered-By headers at the application and the reverse proxy.
- Disable directory listing and block access to backup, temp and version-control files.
- Do not ship source maps, comments or bundles that contain internal hostnames or credentials.
- Return only the fields a caller needs from APIs, and review new fields for sensitive data.
- Add an automated check that fails the build if a debug route is registered in production.
If it already happened
Disable the exposed debug route or verbose handler, redeploy, and strip banners at the proxy while the permanent fix is prepared.
Rotate any secret, key or token that appeared in a response, log or client bundle, and purge cached copies where you can.
Confirm with a test that errors return a generic message, debug routes are unreachable and headers carry no version detail.
Add a production-config checklist and automated header, route and error-response checks to the release pipeline.
Check yourself
1. What is the safest way to report an unhandled server error to a user?
2. Why is an X-Powered-By header that shows a framework version a finding?
3. A debug route that dumps process configuration is reachable in production. What is the right fix?