HIGH data exposureactix

Data Exposure in Actix

How Data Exposure Manifests in Actix

Data exposure in Actix applications typically occurs when sensitive information is inadvertently returned in API responses or error messages. This vulnerability can manifest through several Actix-specific patterns:

  • Debug mode leakage: When Actix runs in debug mode, it may include stack traces and internal server details in error responses, exposing file paths, environment variables, and application structure.
  • Default error handlers: Actix's default error handlers can reveal internal state through detailed error messages that include database query information or request context.
  • Logging configuration issues: Actix applications often log request bodies or headers that may contain sensitive data like passwords, tokens, or personal information.
  • Serialization of internal structs: Accidentally serializing internal data structures that contain sensitive fields when only a subset should be exposed.

Here's a common Actix pattern that leads to data exposure:

async fn get_user_profile(info: web::Path<(String, String)>) -> impl Responder {
    let (username, token) = info.into_inner();
    
    // Database query that might fail
    let user = sqlx::query_as::<_, User>("SELECT * FROM users WHERE username = $1")
        .bind(username)
        .fetch_one('db)
        .await;
        
    match user {
        Ok(user) => {
            // Exposing internal database structure directly
            HttpResponse::Ok().json(user)
        }
        Err(_) => {
            // Debug mode may expose internal error details
            HttpResponse::InternalServerError().body("Database error occurred")
        }
    }
}

In this example, the User struct likely contains fields like password_hash, internal_ids, or other sensitive information that should never be exposed to API consumers. The error handler also doesn't sanitize potential database errors.

Actix-Specific Detection

Detecting data exposure in Actix applications requires examining both the code structure and runtime behavior. Here are Actix-specific detection methods:

Code Analysis

Look for these patterns in your Actix codebase:

// Dangerous: Exposing internal struct directly
#[derive(Serialize)]
struct User {
    id: i32,
    username: String,
    email: String,
    password_hash: String, // This should never be exposed!
    ssn: String, // Sensitive data exposure
}

// Safer: Using a dedicated response DTO
#[derive(Serialize)]
struct UserProfile {
    id: i32,
    username: String,
    email: String,
}

Scan your Actix handlers for:

  • Direct serialization of database models or internal structs
  • Missing #[serde(skip)] attributes on sensitive fields
  • Debug mode enabled in production (check RUST_LOG and environment variables)
  • Default error handlers without custom sanitization

Runtime Testing

middleBrick's black-box scanning can detect data exposure by:

  1. Analyzing API responses for sensitive data patterns (passwords, tokens, SSNs, credit card numbers)
  2. Checking for stack traces and internal error messages in responses
  3. Testing error conditions to see what information is revealed
  4. Scanning OpenAPI specs for fields marked as sensitive but still included in responses

You can also test manually by:

# Test with invalid input to trigger error responses
curl -X POST http://localhost:8080/api/login \
  -H "Content-Type: application/json" \
  -d '{"username":"test","password":"wrong"}'

# Check if debug information is exposed
curl -X GET http://localhost:8080/nonexistent-endpoint

Look for responses containing:

  • Stack traces or file paths
  • Database error details
  • Internal IDs or system information
  • Default Actix error messages that reveal implementation details

Actix-Specific Remediation

Remediating data exposure in Actix requires a combination of code patterns and configuration changes. Here are Actix-specific solutions:

1. Use Dedicated Response DTOs

Never serialize internal structs directly. Create specific response types:

// Internal model (kept private)
#[derive(Debug, sqlx::FromRow)]
struct User {
    id: i32,
    username: String,
    email: String,
    password_hash: String,
    ssn: String,
    internal_id: Uuid,
}

// Public response model
#[derive(Serialize)]
struct UserResponse {
    id: i32,
    username: String,
    email: String,
}

// Handler with proper mapping
async fn get_user_profile(
    path: web::Path<i32>, 
    pool: web::Data<PgPool>
) -> Result<impl Responder> {
    let user_id = path.into_inner();
    
    let user = sqlx::query_as::<_, User>("SELECT * FROM users WHERE id = $1")
        .bind(user_id)
        .fetch_one('pool)
        .await?;
        
    let response = UserResponse {
        id: user.id,
        username: user.username,
        email: user.email,
    };
    
    Ok(HttpResponse::Ok().json(response))
}

2. Custom Error Handling

Replace Actix's default error handlers with sanitized versions:

use actix_web::{error, dev, http::StatusCode, HttpResponse};
use serde::Serialize;

#[derive(Serialize)]
struct ErrorResponse {
    error: String,
    message: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    request_id: Option<String>,
}

pub fn create_error_handler() -> error::ErrorHandler<actix_web::Error> {
    error::ErrorHandler::new(|err, req| {
        let request_id = uuid::Uuid::new_v4().to_string();
        
        // Log the actual error internally
        log::error!("Request {} failed: {:?}", request_id, err);
        
        // Return sanitized response
        let response = ErrorResponse {
            error: "internal_error".to_string(),
            message: "An internal error occurred. Please try again later.".to_string(),
            request_id: Some(request_id),
        };
        
        Ok(HttpResponse::build(StatusCode::INTERNAL_SERVER_ERROR)
            .content_type("application/json")
            .body(serde_json::to_string(&response).unwrap()))
    })
}

// Register in main
#[actix_web::main]
async fn main() -> std::io::Result<()> {
    HttpServer::new(|| {
        App::new()
            .wrap(create_error_handler())
            .service(user_profile)
    })
    .bind("127.0.0.1:8080")?
    .run()
    .await
}

3. Field-Level Serialization Control

Use serde attributes to control field exposure:

#[derive(Serialize)]
struct User {
    id: i32,
    username: String,
    email: String,
    #[serde(skip)]
    password_hash: String,
    #[serde(skip)]
    ssn: String,
    #[serde(skip)]
    internal_id: Uuid,
    
    #[serde(rename = "createdAt")]
    created_at: DateTime<Utc>,
}

4. Configure Debug Mode

Ensure debug mode is disabled in production:

// In main.rs or lib.rs
#[actix_web::main]
async fn main() -> std::io::Result<()> {
    std::env::set_var("RUST_LOG", "info");
    std::env::set_var("RUST_BACKTRACE", "0");
    
    HttpServer::new(|| {
        App::new()
            .wrap(middleware::Logger::default())
            .service(user_profile)
    })
    .bind("127.0.0.1:8080")?
    .run()
    .await
}

5. Input Validation and Sanitization

Validate and sanitize all inputs before processing:

use actix_web::{web, FromRequest, HttpResponse};
use serde::Deserialize;
use validator::Validate;

#[derive(Deserialize, Validate)]
struct LoginRequest {
    #[validate(email)]
    email: String,
    #[validate(length(min = 8))]
    password: String,
}

async fn login(
    login_data: web::Json<LoginRequest>,
    pool: web::Data<PgPool>
) -> Result<impl Responder> {
    // Validate input
    login_data.validate()?;
    
    // Process without logging sensitive data
    let user = authenticate_user(&login_data.email, &login_data.password, 'pool).await?;
    
    // Return only necessary data
    Ok(HttpResponse::Ok().json(&user.public_profile()))
}

Related CWEs: dataExposure

CWE IDNameSeverity
CWE-200Exposure of Sensitive Information HIGH
CWE-209Error Information Disclosure MEDIUM
CWE-213Exposure of Sensitive Information Due to Incompatible Policies HIGH
CWE-215Insertion of Sensitive Information Into Debugging Code MEDIUM
CWE-312Cleartext Storage of Sensitive Information HIGH
CWE-359Exposure of Private Personal Information (PII) HIGH
CWE-522Insufficiently Protected Credentials CRITICAL
CWE-532Insertion of Sensitive Information into Log File MEDIUM
CWE-538Insertion of Sensitive Information into Externally-Accessible File HIGH
CWE-540Inclusion of Sensitive Information in Source Code HIGH

Frequently Asked Questions

How does middleBrick detect data exposure in Actix applications?
middleBrick performs black-box scanning by sending requests to your Actix endpoints and analyzing the responses. It looks for sensitive data patterns like passwords, tokens, SSNs, and credit card numbers in JSON responses. The scanner also checks for stack traces, internal error messages, and debug information that might be exposed when Actix encounters errors. Additionally, middleBrick can analyze your OpenAPI/Swagger specs to identify fields marked as sensitive but still included in API responses.
Can middleBrick scan my Actix API that uses async/await patterns?
Yes, middleBrick works with any HTTP API regardless of the underlying framework or async patterns. It sends standard HTTP requests to your Actix endpoints and analyzes the responses. The scanner doesn't need to understand your async/await code structure—it treats your API as a black box, testing the unauthenticated attack surface just like any external client would. This makes it framework-agnostic and effective for Actix applications using async runtimes like tokio.