Denial Of Service in Actix with Mutual Tls
Denial Of Service in Actix with Mutual Tls — how this specific combination creates or exposes the vulnerability
Mutual Transport Layer Security (mTLS) in Actix requires both the client and server to present valid certificates during the TLS handshake. While mTLS strengthens identity verification, it introduces additional processing overhead that can amplify Denial of Service (DoS) risks compared to one-way TLS. The combination creates exposure in three dimensions: handshake cost, connection handling, and request parsing.
During the TLS handshake, mTLS involves additional cryptographic operations such as client certificate validation, revocation checks (e.g., CRL or OCSP), and signature verification. In Actix, if these operations are performed on the accepting actor for every connection without tuning, the CPU cost per handshake can become significant under high concurrency. An attacker can open many concurrent TLS connections and force the server to perform expensive mTLS handshakes, exhausting CPU or worker threads. This is a resource exhaustion vector that manifests as a high-severity risk in the Authentication and Rate Limiting checks of a middleBrick scan.
Connection lifecycle management also contributes to DoS potential. Actix applications that accept TLS streams but do not enforce strict timeouts for the TLS handshake or early data can leave sockets in a half-open state. If the server waits indefinitely for client certificates or application-level HTTP data, file descriptors and memory buffers may be held by idle connections. In a scenario where the server also parses request bodies before authorization, an attacker might send partial or malformed requests that consume buffers. The BOLA/IDOR and Input Validation checks in middleBrick highlight cases where unauthenticated endpoints accept large or malformed payloads, increasing the likelihood of resource saturation.
The interplay of mTLS and HTTP semantics in Actix can exacerbate parsing-based DoS. For example, if the server uses permissive content-type handling or large default payload limits, an attacker can send carefully crafted requests that trigger expensive deserialization or body accumulation. This amplifies the impact of vulnerabilities flagged by the Property Authorization and Unsafe Consumption checks. middleBrick detects these patterns by correlating TLS-level metadata with runtime behavior, noting cases where unauthenticated endpoints exhibit unusually high resource consumption during probing.
Mutual Tls-Specific Remediation in Actix — concrete code fixes
Apply mTLS hardening and resource controls in Actix to reduce DoS exposure. These examples assume you are using native Rust TLS with acceptor configuration; adapt as needed for your runtime.
1. Limit handshake concurrency and set timeouts
Restrict the number of concurrent TLS handshakes and enforce deadlines to prevent resource exhaustion. Use Actix server builder settings and Tokio timeouts to bound connection setup cost.
use actix_web::{web, App, HttpServer};
use std::time::Duration;
use tokio_rustls::rustls::ServerConfig;
use tokio_rustls::TlsAcceptor;
use std::sync::Arc;
async fn config_tls() -> Arc<TlsAcceptor> {
let mut config = ServerConfig::builder()
.with_safe_defaults()
.with_no_client_auth() // will be replaced with client auth
.with_client_cert_verifier(Arc::new(|_end_entity, _intermediates| Ok(()))); // customize as needed
// Load server cert and key
config.set_single_cert(vec![], vec![]); // provide certs and key in real code
let tls_acceptor = TlsAcceptor::from(Arc::new(config));
Arc::new(tls_acceptor)
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
let tls_acceptor = config_tls().await;
HttpServer::new(move || {
App::new()
.wrap(actix_web::middleware::Logger::default())
.service(web::resource("/")
.route(web::get().to(|| async { "ok" })))
})
.bind_rustls(
"127.0.0.1:8443",
tls_acceptor.clone(),
)?
.accept_timeout(Duration::from_secs(5)) // limit handshake time
.max_http_version1(100) // bound concurrent connections as appropriate
.run()
.await
}
2. Enforce client certificate validation constraints
Validate client certificates efficiently and avoid overly permissive verifications that can be forced to consume excessive cycles. Prefer stapled OCSP where available and set reasonable verification depth.
use rustls::{ClientCertVerified, ServerCertVerified, TLSError};
use std::time::SystemTime;
fn client_cert_verifier() -> Arc<dyn Fn(Vec<rustls::Certificate>, &rustls::WebPkiDNServerSession) -> Result<ClientCertVerified, TLSError>> {
Arc::new(|certs, _session| {
// Implement short-circuit checks to reduce CPU per handshake
if certs.is_empty() {
return Err(TLSError::NoCertificatesPresented);
}
// Example: basic validity window check to avoid heavy CRL/OCSP in hot path
// In production, use a verifier that leverages stapling and cached results
Ok(ClientCertVerified::assertion())
})
}
3. Apply request size and body limits early
Reject or close connections that exceed payload thresholds before consuming significant buffer space. Configure payload limits at the Actix app or service level to protect parsing stages flagged by Property Authorization and Unsafe Consumption checks.
use actix_web::{middleware, web, App, HttpServer};
#[actix_web::main]
async fn main() -> std::io::Result<()> {
HttpServer::new(|| {
App::new()
.wrap(middleware::Logger::default())
.configure(|cfg| {
cfg.service(
web::resource("/api/upload")
.route(web::post().to(|| async { "upload" }))
.app_data(web::JsonConfig::default().limit(4096)) // limit JSON body
.app_data(web::PayloadConfig::new(64 * 1024)), // overall payload cap
);
})
})
.bind_rustls(
"0.0.0.0:8443",
tls_acceptor().await,
)?
.run()
.await
}
4. Use connection and rate limits aligned with mTLS cost
Configure per-IP connection and request rate limits to mitigate handshake amplification. Combine with middleware to enforce policies before expensive certificate operations are triggered.
use actix_web::middleware::errhandlers::ErrorHandlers;
use actix_web::dev::ServiceRequest;
use actix_web::error::ErrorTooManyRequests;
use actix_web::Either;
fn rate_limited_app() -> App {
App::new()
.wrap(ErrorHandlers::new().handler(actix_web::http::StatusCode::TOO_MANY_REQUESTS, |res| {
async move { Either::Left(res.error_response()) }
}))
.service(web::resource("/").to(|| async { "limited" }))
}
Related CWEs: resourceConsumption
| CWE ID | Name | Severity |
|---|---|---|
| CWE-400 | Uncontrolled Resource Consumption | HIGH |
| CWE-770 | Allocation of Resources Without Limits | MEDIUM |
| CWE-799 | Improper Control of Interaction Frequency | MEDIUM |
| CWE-835 | Infinite Loop | HIGH |
| CWE-1050 | Excessive Platform Resource Consumption | MEDIUM |