Skip to content
Error handling Reviewed 2026-09-13

Information disclosure through errors and debug output

An error response can reveal credentials, private records, filesystem paths, database queries, or internal configuration. The significance depends on the information exposed and who receives it. A framework name by itself is not equivalent to a leaked authentication token.

Trust boundary: diagnostic details intended for operators must not automatically become client-visible output. Keep a useful, documented error contract for users and protected, appropriately redacted diagnostics for operators. Do not remove all production logs: that can hide failures and security events. OWASP's error-handling guidance recommends separating public responses from server-side investigation.

Unsafe example: returning the raw exception

// An Express error-handler fragment.
res.status(500).json({error: err.message, stack: err.stack});

Exception text can include input values, connection details, or secrets supplied by a dependency. Encoding it as JSON does not make it suitable for disclosure.

Safer example: a generic incident response

This CommonJS factory creates an Express 4/5 error handler. The supplied logger accepts structured events and must handle its own delivery failures without throwing or adding raw request/error objects automatically.

const {randomUUID} = require('node:crypto');

function unexpectedErrorHandler(logger) {
  return function (err, req, res, next) {
    if (res.headersSent) return next(err);
    const incidentId = randomUUID();
    logger.error({event: 'request_failed', incidentId});
    return res.status(500)
      .set('Cache-Control', 'no-store')
      .json({error: 'The request could not be completed.', incidentId});
  };
}

Register it after routes and after handlers for expected validation/authentication failures. The response identifier is generated by the server and matches the event in the log. The snippet intentionally records neither the exception message nor the request body. If investigation needs more detail, define approved diagnostic fields and a redaction policy in a restricted logging path. Log access, retention, and downstream APM capture still matter.

When response headers have already been sent, delegate rather than attempting a second JSON response. Ensure the fallback handler also has production-safe behavior; Express's default error handler changes its output according to the environment. See the Express error-handling guide.

Rails: keep detailed exception pages out of production

# Unsafe for a publicly accessible production environment.
config.consider_all_requests_local = true
# Safer production setting; configure and test the public error pages too.
config.consider_all_requests_local = false

This setting is only one layer. Custom rescue handlers, reverse proxies, and debugging endpoints may still reveal details. Keep useful sanitized logs and inspect the effective deployed configuration. The Rails configuration guide explains the setting.

Regression test

Create a local fictional exception containing a distinctive fake secret and internal-path marker. Pass it to the handler with a fake logger/response. Assert status 500, a generic message, no-store, and a shared incident ID; assert neither marker appears in the public response or structured event. Cover the headers-already-sent branch and expected 4xx responses separately. In an integration test, confirm proxy, APM, and fallback error output obey the same disclosure policy.

Related: sensitive logging, exception handling, and hardcoded-secret response.

References: CWE-209 — error messages containing sensitive information and CWE-200 — exposure of sensitive information to an unauthorized actor.