HIGH excessive data exposureactix

Excessive Data Exposure in Actix

How Excessive Data Exposure Manifests in Actix

Excessive Data Exposure in Actix applications typically occurs through several Actix-specific patterns. The most common scenario involves returning entire database models or structs directly from handlers without filtering sensitive fields. For example, when using Actix's derive macros with serde, developers often return entire structs like this:

#[derive(Serialize)]
struct User {
    id: i32,
    email: String,
    password_hash: String, // Sensitive!
    ssn: String, // Sensitive!
    credit_card: String, // Sensitive!
}

async fn get_user(id: web::Path) -> impl Responder {
    let user = User::find_by_id(id).await.unwrap();
    HttpResponse::Ok().json(user) // Returns ALL fields!
}

This pattern is particularly dangerous in Actix because the framework's derive macros make it trivial to expose entire structs. Another Actix-specific manifestation occurs with the web::Json extractor, which automatically serializes entire structs:

async fn create_user(user: web::Json) -> impl Responder {
    // Entire User struct is deserialized, including sensitive fields
    // Then potentially returned or logged
    HttpResponse::Created().json(user.0)
}

Actix's middleware system can also contribute to data exposure. Custom middleware that logs request bodies or responses might inadvertently capture sensitive data. For instance:

async fn log_middleware(
    req: actix_web::dev::ServiceRequest,
    srv: &mut actix_web::dev::Service,
) -> Result {
    let response = srv.call(req).await?;
    
    // This logs the entire response body, including sensitive data
    if let Ok(body) = response.clone().into_body().try_into_bytes() {
        log::info!("Response: {}", String::from_utf8_lossy(&body));
    }
    
    Ok(response)
}

Actix's query parameter handling can also lead to exposure. When using web::Query to deserialize query parameters into structs, all fields become accessible, potentially exposing internal implementation details:

#[derive(Deserialize)]
struct SearchParams {
    query: String,
    user_id: i32, // Internal ID exposed to client
    include_sensitive: bool, // Dangerous flag
}

async fn search(params: web::Query<SearchParams>) -> impl Responder {
    // Client can manipulate internal fields
    if params.include_sensitive {
        // Returns sensitive data based on client-controlled flag
    }
}

Actix-Specific Detection

Detecting Excessive Data Exposure in Actix applications requires examining both the code structure and runtime behavior. In Actix applications, you can identify this issue by looking for patterns where entire structs are returned without field filtering. Using middleBrick's CLI, you can scan your Actix endpoints:

npx middlebrick scan https://api.example.com/actix-endpoint

middleBrick's scanner will identify endpoints that return complete structs and flag potential data exposure. The scanner specifically looks for Actix's derive macro patterns and web::Json usage that might expose sensitive fields.

Manual code review in Actix applications should focus on these patterns:

  • Search for #[derive(Serialize)] on structs that contain sensitive fields
  • Look for direct json() returns of entire structs
  • Examine middleware that logs or processes request/response bodies
  • Check for web::Query deserialization into structs with internal fields

For runtime detection, Actix's logging can help identify what data is being transmitted. Configure detailed logging to see response bodies:

use actix_web::middleware::Logger;

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    env_logger::init_from_env(env_logger::Env::new().default_filter_or("info"));
    
    HttpServer::new(|| {
        App::new()
            .wrap(Logger::default())
            .service(get_user)
    })
    .bind("127.0.0.1:8080")?.run().await
}

This logging will show you exactly what data your Actix endpoints are returning, making it easier to spot excessive exposure. For automated scanning in CI/CD, use middleBrick's GitHub Action:

name: API Security Scan

on: [push, pull_request]

jobs:
  security:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v2
    - name: Run middleBrick Scan
      uses: middlebrick/middlebrick-action@v1
      with:
        url: ${{ secrets.API_URL }}
        fail-on-severity: high

Actix-Specific Remediation

Remediating Excessive Data Exposure in Actix requires a multi-layered approach. The most effective strategy is to create dedicated response DTOs (Data Transfer Objects) that expose only the necessary fields:

// Original struct with sensitive fields
#[derive(Deserialize, Serialize)]
struct User {
    id: i32,
    email: String,
    password_hash: String,
    ssn: String,
    credit_card: String,
}

// Safe response DTO
#[derive(Serialize)]
struct UserResponse {
    id: i32,
    email: String,
}

async fn get_user(id: web::Path<i32>) -> impl Responder {
    let user = User::find_by_id(id).await.unwrap();
    let response = UserResponse {
        id: user.id,
        email: user.email.clone(),
    };
    HttpResponse::Ok().json(response)
}

For Actix applications using async-std or tokio, you can leverage async mapping to transform data before returning:

async fn get_users() -> impl Responder {
    let users = User::find_all().await.unwrap();
    
    // Transform to safe DTOs
    let safe_users: Vec<UserResponse> = users
        .into_iter()
        .map(|u| UserResponse {
            id: u.id,
            email: u.email.clone(),
        })
        .collect();
        
    HttpResponse::Ok().json(safe_users)
}

Actix's middleware system can help enforce data exposure policies. Create a middleware that automatically filters sensitive fields:

use actix_web::dev::{Service, ServiceRequest, ServiceResponse, Transform};
use futures_util::future::{ready, Ready};
use serde_json::Value;

struct DataExposureFilter;

impl<S, B> Transform<S> for DataExposureFilter
where
    S: Service<Request = ServiceRequest, Response = ServiceResponse<B>>,
    B: actix_web::dev::MessageBody,
{
    type Request = ServiceRequest;
    type Response = ServiceResponse<Body>;
    type Error = S::Error;
    type InitError = ();
    type Transform = DataExposureMiddleware<S>;
    type Future = Ready<Result<Self::Transform, Self::InitError>>;

    fn new_transform(&self, service: S) -> Self::Future {
        ready(Ok(DataExposureMiddleware { service }))
    }
}

struct DataExposureMiddleware<S> {
    service: S,
}

impl<S, B> Service for DataExposureMiddleware<S>
where
    S: Service<Request = ServiceRequest, Response = ServiceResponse<B>>,
    B: actix_web::dev::MessageBody,
{
    type Request = ServiceRequest;
    type Response = ServiceResponse<Body>;
    type Error = S::Error;
    type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>>>>;

    fn poll_ready(&mut self, cx: &mut Context) -> Poll<Result<(), Self::Error>> {
        self.service.poll_ready(cx)
    }

    fn call(&mut self, req: ServiceRequest) -> Self::Future {
        let service_future = self.service.call(req);
        
        Box::pin(async move {
            let response = service_future.await?;
            
            // Check if response is JSON and filter sensitive fields
            if let Ok(body) = response.clone().into_body().try_into_bytes() {
                if let Ok(json) = serde_json::from_slice::(&body) {
                    let filtered = filter_sensitive_data(json);
                    let new_body = Body::from(filtered.to_string());
                    return Ok(response.map_body(|_, _| new_body));
                }
            }
            
            Ok(response)
        })
    }
}

fn filter_sensitive_data(json: Value) -> Value {
    // Implement field filtering logic here
    json
}

For Actix applications using SQLx or other database libraries, ensure you only select the fields you need:

async fn get_user(id: web::Path<i32>) -> impl Responder {
    let user = sqlx::query_as::<_, User>(
        "SELECT id, email FROM users WHERE id = $1"
    )
    .bind(id)
    .fetch_one(&pool)
    .await?;
    
    HttpResponse::Ok().json(user)
}

Related CWEs: propertyAuthorization

CWE IDNameSeverity
CWE-915Mass Assignment HIGH

Frequently Asked Questions

How does middleBrick specifically detect Excessive Data Exposure in Actix applications?
middleBrick scans Actix endpoints by examining the response structure and identifying patterns where entire structs are returned without field filtering. It looks for Actix-specific patterns like direct serialization of models, use of derive macros on structs containing sensitive fields, and middleware that might log or expose sensitive data. The scanner tests endpoints with various inputs to see what data is actually returned, flagging any sensitive information like passwords, SSNs, or internal IDs that shouldn't be exposed to clients.
Can middleBrick scan Actix applications that use async-std or tokio runtimes?
Yes, middleBrick works with any Actix application regardless of the async runtime. The scanner operates at the HTTP level, testing the actual API endpoints rather than the internal implementation. Whether your Actix app uses tokio, async-std, or any other runtime, middleBrick will scan the exposed endpoints and identify Excessive Data Exposure issues by analyzing the JSON responses and HTTP responses for sensitive data patterns.