HIGH denial of serviceactix

Denial Of Service in Actix

How Denial Of Service Manifests in Actix

Denial of Service (DoS) attacks against Actix applications typically exploit the framework's async runtime and request handling patterns. Unlike traditional synchronous servers, Actix's Tokio-based runtime creates opportunities for resource exhaustion through specific attack vectors.

The most common DoS pattern in Actix involves slowloris-style attacks where clients establish connections but send data extremely slowly. Actix's default timeout settings are often too permissive, allowing attackers to tie up worker threads for extended periods. A malicious client can send partial HTTP requests and keep connections open indefinitely, consuming Tokio's thread pool capacity.

use actix_web::{web, App, HttpServer, Responder};
use std::time::Duration;

async fn vulnerable_endpoint() -> impl Responder {
    // This endpoint has no timeout protection
    tokio::time::sleep(Duration::from_secs(60)).await;
    "response"
}

#[actix_web::main]
async fn main() {
    HttpServer::new(|| {
        App::new()
            .service(web::resource("/vulnerable")
                .route(web::get().to(vulnerable_endpoint)))
    })
    .bind("127.0.0.1:8080")?.run()
    .await;
}

This code creates a perfect DoS target. An attacker can flood the server with requests to /vulnerable, each holding a worker thread for 60 seconds. With default Actix settings (typically 16 worker threads), just 16 concurrent requests can completely block the server.

Another critical Actix DoS vector involves unbounded request body reading. Actix's default extractor will read entire request bodies into memory without limits, making the application vulnerable to memory exhaustion attacks:

use actix_web::{web, App, HttpServer, Responder};
use serde::Deserialize;

#[derive(Deserialize)]
struct MaliciousPayload {
    data: String,
}

async fn unbounded_body(payload: web::Json<MaliciousPayload>) -> impl Responder {
    // No size limit on the JSON body
    format!("Received: {}", payload.data.len())
}

#[actix_web::main]
async fn main() {
    HttpServer::new(|| {
        App::new()
            .service(web::resource("/unbounded")
                .route(web::post().to(unbounded_body)))
    })
    .bind("127.0.0.1:8080")?.run()
    .await;
}

An attacker can send a 10GB JSON payload, causing Actix to attempt loading it entirely into memory, potentially crashing the application or causing the OS to kill it for memory exhaustion.

Actix's middleware system can also introduce DoS vulnerabilities when middleware performs expensive operations without proper safeguards. Consider a logging middleware that processes request bodies:

use actix_web::{dev::ServiceRequest, dev::ServiceResponse, HttpServer, App, Error};
use actix_web::middleware::Middleware;
use futures_util::future::LocalBoxFuture;
use std::pin::Pin;

struct ExpensiveLogger;

impl Middleware for ExpensiveLogger
where
    S: actix_web::dev::Service>,
    S::Future: 'static,
    S::Error: 'static,
    B: actix_http::body::MessageBody + 'static,
{
    type Response = ServiceResponse;
    type Error = Error;
    type InitError = ();
    type Future = LocalBoxFuture<'static, Result>;
    type Start = ();

    fn start(&self, _req: &ServiceRequest) -> Result<Self::Start, Self::InitError> {
        Ok(())
    }

    fn call(&self, req: ServiceRequest, srv: S) -> Self::Future {
        // No timeout or resource limits
        Box::pin(async move {
            let res = srv.call(req).await?;
            // Expensive operation without limits
            let body = res.body().clone();
            let _ = body.as_bytes().unwrap().len();
            Ok(res)
        })
    }
}

This middleware processes every request body without any rate limiting or timeout, creating a perfect amplification vector for DoS attacks.

Actix-Specific Detection

Detecting DoS vulnerabilities in Actix applications requires understanding both the framework's architecture and common attack patterns. middleBrick's API security scanner includes specialized detection for Actix-specific DoS vectors.

For Actix applications, middleBrick performs several key detection operations:

Timeout Configuration Analysis: The scanner examines HTTP server configurations to identify missing or excessive timeout values. Actix applications should configure read, write, and keep-alive timeouts appropriately. The scanner checks for default or missing timeout configurations that leave applications vulnerable to slowloris attacks.

Request Size Limiting: middleBrick analyzes endpoint configurations to detect missing payload size limits. In Actix, the web::Json and web::Bytes extractors should have explicit size limits configured. The scanner verifies that these limits exist and are set to reasonable values based on the application's requirements.

Resource Usage Monitoring: During scanning, middleBrick simulates high-load conditions to observe how Actix applications handle concurrent requests. The scanner measures response times, connection handling, and resource utilization to identify potential DoS vulnerabilities.

Middleware Security Analysis: The scanner examines middleware chains for expensive operations without proper safeguards. Actix middleware that processes request bodies, performs external calls, or executes complex logic without timeouts represents a DoS risk.

Here's how you can use middleBrick to scan your Actix application:

// Install middleBrick CLI
npm install -g middlebrick

// Scan your Actix API endpoint
middlebrick scan https://your-actix-app.com/api/endpoint

// Example output showing DoS vulnerabilities
{
  "risk_score": 45,
  "grade": "D",
  "findings": [
    {
      "category": "Rate Limiting",
      "severity": "high",
      "title": "Missing request size limits",
      "remediation": "Configure web::Json<T> with explicit size limits using Config::limit()"
    },
    {
      "category": "Timeout Protection",
      "severity": "high",
      "title": "Excessive default timeouts",
      "remediation": "Set appropriate read/write timeouts using Server::timeout()"
    }
  ]
}

middleBrick's continuous monitoring plan can automatically scan your Actix APIs on a schedule, alerting you when new DoS vulnerabilities are detected or when security scores drop below your configured thresholds.

Actix-Specific Remediation

Securing Actix applications against DoS attacks requires implementing multiple defensive layers. Here are Actix-specific remediation strategies with working code examples.

Configure Request Timeouts: Set appropriate timeouts for all HTTP operations to prevent slowloris attacks:

use actix_web::{web, App, HttpServer, Responder};
use std::time::Duration;

async fn secure_endpoint() -> impl Responder {
    // Process request with timeout protection
    "secure response"
}

#[actix_web::main]
async fn main() {
    HttpServer::new(|| {
        App::new()
            .service(web::resource("/secure")
                .route(web::get().to(secure_endpoint)))
    })
    .timeout(Duration::from_secs(10)) // Read timeout
    .keep_alive(Duration::from_secs(5)) // Keep-alive timeout
    .bind("127.0.0.1:8080")?.run()
    .await;
}

Implement Request Size Limits: Configure payload size limits for all extractors:

use actix_web::{web, App, HttpServer, Responder};
use actix_web::web::JsonConfig;

#[derive(serde::Deserialize)]
struct SecurePayload {
    data: String,
}

async fn size_limited_endpoint(payload: web::Json<SecurePayload>) -> impl Responder {
    format!("Processed: {}", payload.data.len())
}

#[actix_web::main]
async fn main() {
    let json_config = JsonConfig::default()
        .limit(1024 * 1024) // 1MB limit
        .error_handler(|err, _req| {
            actix_web::error::ErrorBadRequest("Payload too large")
        });

    HttpServer::new(|| {
        App::new()
            .app_data(json_config)
            .service(web::resource("/size-limited")
                .route(web::post().to(size_limited_endpoint)))
    })
    .bind("127.0.0.1:8080")?.run()
    .await;
}

Add Rate Limiting: Implement rate limiting to prevent request flooding:

use actix_web::{web, App, HttpServer, Responder};
use actix_web::middleware::errhandlers::ErrorHandlerResponse;
use actix_web::dev::ServiceResponse;
use actix_web::http::StatusCode;
use actix_ratelimit::RateLimiter;
use std::time::Duration;

async fn rate_limited_endpoint() -> impl Responder {
    "processed"
}

#[actix_web::main]
async fn main() {
    HttpServer::new(|| {
        App::new()
            .wrap(
                RateLimiter::middleware(
                    // 100 requests per minute per IP
                    actix_ratelimit::MemoryStore::new(Duration::from_secs(60)),
                    actix_ratelimit::RateQuota::new(100, Duration::from_secs(60)),
                )
                .with_err_handler(|_err, _req| {
                    ErrorHandlerResponse::Response(ServiceResponse::new(
                        _req.head().clone(),
                        actix_http::Response::build(StatusCode::TOO_MANY_REQUESTS)
                            .body("Rate limit exceeded"),
                    ))
                }),
            )
            .service(web::resource("/rate-limited")
                .route(web::get().to(rate_limited_endpoint)))
    })
    .bind("127.0.0.1:8080")?.run()
    .await;
}

Implement Circuit Breakers: Add circuit breaker patterns for external service calls:

use actix_web::{web, App, HttpServer, Responder};
use actix_web::middleware::errhandlers::ErrorHandlerResponse;
use actix_web::dev::ServiceResponse;
use actix_web::http::StatusCode;
use actix_circuitbreaker::{CircuitBreaker, TripWhen, TripWhenAll};
use std::time::Duration;

async fn external_service_call() -> Result<String, ()> {
    // Simulate external service call
    tokio::time::sleep(Duration::from_millis(500)).await;
    Ok("external data".to_string())
}

async fn protected_endpoint() -> impl Responder {
    let circuit_breaker = CircuitBreaker::builder(
        TripWhen::when_all([
            TripWhen::new(|err: &Result<String, ()>| err.is_err(), 5), // Trip after 5 failures
            TripWhen::new(|_res: &Result<String, ()>| false, 0), // No success threshold
        ]),
        Duration::from_secs(30), // Reset after 30 seconds
    )
    .build();

    match circuit_breaker.call(|| external_service_call()).await {
        Ok(data) => data,
        Err(_) => "service unavailable".to_string(),
    }
}

#[actix_web::main]
async fn main() {
    HttpServer::new(|| {
        App::new()
            .service(web::resource("/protected")
                .route(web::get().to(protected_endpoint)))
    })
    .bind("127.0.0.1:8080")?.run()
    .await;
}

Memory Safety Measures: Implement streaming for large payloads instead of loading into memory:

use actix_web::{web, App, HttpServer, Responder};
use bytes::BytesMut;
use futures_util::stream::StreamExt;

async fn streaming_endpoint(mut payload: web::Payload) -> impl Responder {
    let mut body = BytesMut::new();
    
    while let Some(chunk) = payload.next().await {
        let chunk = chunk.unwrap();
        if body.len() + chunk.len() > 1024 * 1024 { // 1MB limit
            return actix_web::HttpResponse::build(StatusCode::PAYLOAD_TOO_LARGE)
                .finish();
        }
        body.extend_from_slice(&chunk);
    }
    
    format!("Processed {} bytes", body.len())
}

#[actix_web::main]
async fn main() {
    HttpServer::new(|| {
        App::new()
            .service(web::resource("/streaming")
                .route(web::post().to(streaming_endpoint)))
    })
    .bind("127.0.0.1:8080")?.run()
    .await;
}

These remediation strategies significantly reduce the DoS attack surface of Actix applications by implementing proper timeout handling, size limits, rate limiting, and circuit breakers.

Related CWEs: resourceConsumption

CWE IDNameSeverity
CWE-400Uncontrolled Resource Consumption HIGH
CWE-770Allocation of Resources Without Limits MEDIUM
CWE-799Improper Control of Interaction Frequency MEDIUM
CWE-835Infinite Loop HIGH
CWE-1050Excessive Platform Resource Consumption MEDIUM

Frequently Asked Questions

How does Actix's async runtime affect DoS vulnerability?
Actix uses Tokio's async runtime with a thread pool for handling requests. Unlike traditional synchronous servers, each request consumes a worker thread for its entire duration. This means 16 concurrent slow requests can block all workers with default settings. The async nature also enables slowloris attacks where clients send partial requests slowly, keeping worker threads occupied without completing requests. Proper timeout configuration is essential to mitigate these risks.
Can middleBrick detect Actix-specific DoS vulnerabilities?
Yes, middleBrick includes specialized detection for Actix applications. The scanner analyzes timeout configurations, request size limits, middleware chains, and resource usage patterns specific to Actix's architecture. It simulates high-load conditions to identify vulnerabilities like missing timeout settings, unbounded payload extraction, and expensive middleware operations. The scanner provides Actix-specific remediation guidance and can be integrated into CI/CD pipelines via the GitHub Action to automatically fail builds when DoS vulnerabilities are detected.