All resources
// Resources

Secure Error Handling and Information Disclosure Review

Published July 4, 2026

Every failure has two audiences. Client needs stable action-oriented response. Operator needs protected diagnostic evidence. Mixing them exposes implementation detail during reconnaissance and frustrates users with opaque failures. CWE-209 describes error messages containing sensitive information about environment, users, or associated data. OWASP Error Handling guidance recommends generic unexpected-error responses while details are recorded server-side. A generic response does not remove need for monitoring, ownership, or remediation.

Define response map

Build error taxonomy around expected client correction, not exception class names. Validation error may identify field and allowed format. Authentication and authorization responses should avoid confirming account or resource state beyond policy. Dependency failure may say retry later, but must not return hostname, provider response, SQL fragment, stack trace, source path, secret, or framework version. Unexpected error returns stable problem identifier and generic message.

Failure classPublic responseEvidence fieldsOwnerPass / fail
Invalid input400, field code, safe guidancerequest ID, field code, rule IDAPI ownerPass: no echoed raw input
Access denied403, generic denialrequest ID, policy ID, outcomeAccess-control ownerFail: resource existence leaked
Upstream unavailable503, retry-safe messagerequest ID, dependency class, timeoutService ownerFail: host, token, or raw upstream body returned
Unexpected exception500, generic messagerequest ID, error class, protected causeRuntime ownerFail: stack trace or file path reaches client

Completed operational artifact: versioned error catalogue with response schema, status mapping, owner, alert threshold, test case, retention target, and evidence-link field. It is reviewed with release changes so new exceptions cannot silently become public API behavior.

Put translation at one boundary

Handle known errors near input validation; translate unknown exceptions at outer request boundary. Code must create a client-safe object while preserving correlation for restricted operations. Example for a worker or API handler:

type Problem = { code: string; message: string; requestId: string };

function internalProblem(requestId: string): Response {
  const body: Problem = { code: "internal_error", message: "Request could not be completed.", requestId };
  return Response.json(body, { status: 500, headers: { "Cache-Control": "no-store" } });
}

try {
  return await performAction(request);
} catch (error) {
  audit("request.failed", { requestId, errorClass: error instanceof Error ? error.name : "unknown" });
  return internalProblem(requestId);
}

Do not serialize error.message, error.stack, request object, database result, or provider response to client. Logging function must follow a redaction contract; otherwise safe HTTP output can still leak through telemetry. Keep debug tooling disabled in production configuration. CWE-209 specifically advises minimal audience-appropriate details and warns that passwords should never be stored in logs.

Test negative paths deliberately

Run production-like tests for malformed JSON, invalid encoding, missing authentication, unauthorized object, unknown route, oversized body, database timeout, DNS failure, exhausted quota, and injected runtime error. Capture client response, headers, log record, and alert behavior. Assert response lacks marker strings representing internal hostname, SQL table, source path, token, and stack frame. Compare errors for existing and nonexistent accounts or resources where policy requires indistinguishable response.

OWASP Web Security Testing Guide supports systematic testing rather than assuming framework defaults are safe. Framework error pages, reverse proxies, serverless platforms, and CDN-generated errors all require review because outer layer can overwrite application response.

Exception and remediation flow

Temporary diagnostic detail must never go to public client. If on-call staff need richer record during incident, owner creates exception containing event scope, protected storage, reader group, start and expiry times, data classes excluded, approval, and removal task. Example: EXC-ERR-011 permits sanitized upstream status and retry count in restricted diagnostic log for 72 hours. It does not permit body, authorization header, or database query. Expiry removes extra fields; owner verifies with fresh failing-path test.

When a leak is found, preserve minimal evidence, disable verbose response, identify exposed fields and affected routes, rotate disclosed credentials, assess log and cache copies, then add regression case. Fix central mapper or configuration rather than one endpoint unless only endpoint owns behavior. Retest public output and restricted diagnostics separately.

Acceptance passes when expected failures give useful safe guidance, unexpected failures use generic stable response, production debug output is disabled, negative-path tests find no markers, logs are access-controlled, and catalogue has owner and remediation state. Failure blocks release for active external leakage; exception needs dated approval and compensating controls. Web testing can validate externally visible error behavior.

Prove public-private split

Send controlled failing request with marker resembling an internal hostname and capture HTTP body, headers, trace identifier, restricted log event, and alert. Expected public result is stable application/problem+json without marker; expected private record carries correlation and sanitized error class. CDN or proxy branded error page that reveals origin details is fail even when handler is correct. Route owner fixes outer configuration; incident owner records exposed audience and retest.

Worked failure test and closure

Exercise one controlled failure from public request through backend dependency. Send invalid synthetic identifier, forced timeout, and unauthorized object request. Expected external responses are stable status class, generic message, request correlation ID where approved, and no stack trace, query, internal hostname, credential, or existence oracle. Internally, correlate same request ID to sanitized error class and owner action. Example command: curl -i https://app.example.test/api/orders/not-a-valid-id; expected output is a bounded client error, not framework diagnostic HTML.

Check differences deliberately. Login, reset, invite, and lookup endpoints often leak whether account or object exists through status, body, timing, or retry behavior. A generic response must still support safe user recovery and accessibility; do not hide every actionable validation message. Distinguish field-format feedback from authentication or authorization facts.

Closure evidence includes request cases, raw external responses with synthetic data, sanitized internal event, error mapping revision, and regression test result. Reopen after framework upgrade, new API gateway policy, exception middleware change, or new dependency integration. Error design is complete only when user receives usable next step and attacker receives no extra system map.

Sources

Have a system that needs testing?