Course contentsShow
← Rust
Lesson 918 of 1,9107. Error Handling PatternsFree lesson

Security and Error Information Leakage

Design robust errors that prevent information disclosure in production.

In this lesson, you will learn how to design error types that distinguish between internal diagnostic needs and public-facing security boundaries to prevent sensitive information leakage.

The Conflict: Debuggability vs. Security

When an application encounters an error, developers often want to include as much context as possible—database connection strings, stack traces, or internal file paths. However, exposing these details in an API response or logs accessible to users is a security vulnerability. An attacker can use these "leaked" details to map your infrastructure, identify vulnerable dependency versions, or guess internal system architecture.

Decoupling Internal and External Representations

To prevent this, you should never return your raw, internal error types directly to the user. Instead, use an Opaque Error Type wrapper that maps internal failures to generic, safe status codes.

Consider this pattern:

pub enum InternalError {
    DatabaseConnectionFailed(String), // Contains sensitive host/credentials
    InvalidInput(String),
}

pub enum PublicError {
    InternalServerError,
    BadRequest,
}

impl PublicError {
    pub fn from_internal(err: InternalError) -> Self {
        // Log the detailed err internally, but return a safe variant to user
        eprintln!("Log detailed error: {:?}", err);
        match err {
            InternalError::DatabaseConnectionFailed(_) => PublicError::InternalServerError,
            InternalError::InvalidInput(_) => PublicError::BadRequest,
        }
    }
}

Best Practices for Leakage Prevention

  1. Centralize Mapping: Implement a conversion layer between your domain errors and your external API response. Never let an std::error::Error trait object pass through to a client-facing boundary.
  2. Sanitize Dynamic Strings: If you must return a message, ensure that fields containing user input are sanitized. Never echo back a raw database exception string, even if it feels harmless.
  3. Environment Awareness: Use conditional logic to show verbose errors only when debug_assertions are enabled or via an explicit RUST_LOG level. In production, always default to a unique "Request ID" or "Correlation ID." This allows you to look up the exact error in your secure, private logs without exposing the details to the user.

By ensuring your public interface only communicates what the user needs to know—and nothing more—you protect your internal architecture from reconnaissance.

Key Takeaway: Always decouple internal diagnostic error data from public error reporting to prevent revealing system internals to unauthorized users.