Course contentsShow
← Node.js
Lesson 1486 of 1,98013. Error Handling and Resilient SystemsFree lesson

Securing Error Responses Against Info Leak

You will ensure that your error responses provide enough detail to the client for debugging without revealing implementation details.

In this lesson, you’ll learn how to craft error responses that assist legitimate clients without handing attackers a roadmap to your internal architecture.

The Information Leak Trap

When a Node.js application encounters an unhandled exception or a deep-level failure, the default behavior—often triggered by frameworks like Express—is to serialize the error object. If you aren't careful, res.status(500).json(err) might leak sensitive implementation details like database schema names, file system paths, internal IP addresses, or the specific version of a library that has a known vulnerability.

An attacker can use this "fingerprinting" to identify your stack and search for relevant exploits.

Implementing the "Opaque Error" Pattern

To secure your responses, you must distinguish between the Internal Error (logged for you) and the Public Error (sent to the client). You should catch errors in a global middleware and normalize the output.

The Defensive Middleware Approach

Instead of passing the raw error object to the client, map it to a generic message while keeping the trace in your immutable logs (as discussed in our lesson on Logging to Immutable Sinks).

app.use((err, req, res, next) => {
  // 1. Log the full stack trace internally for debugging
  console.error(`[Internal Error]: ${err.stack}`);

  // 2. Identify if the error is "Safe" (e.g., Validation Errors)
  if (err.isOperational) {
    return res.status(err.statusCode).json({
      message: err.message // Safe to share client-side errors
    });
  }

  // 3. For all other errors, send an opaque response
  res.status(500).json({
    error: 'Internal Server Error',
    referenceId: req.headers['x-request-id'] // Useful for support
  });
});

By decoupling the error context from the HTTP response, you ensure that even if your database connection drops or a dependency throws a verbose stack trace, the client only sees a standardized, harmless JSON object. Providing a referenceId allows you to correlate the client's report with your private logs without ever exposing the technical reality of the failure.

Key Takeaway: Never expose raw stack traces or internal object properties in your HTTP response; always log the details privately and return a sanitized, high-level message to the client.