HIGH race conditionactixmutual tls

Race Condition in Actix with Mutual Tls

Race Condition in Actix with Mutual Tls — how this specific combination creates or exposes the vulnerability

A race condition in Actix Web when Mutual TLS (mTLS) is used typically arises from a timing window between client certificate validation and the establishment of application-level state. mTLS ensures the client possesses a valid certificate signed by a trusted CA and that the certificate matches the expected subject or SAN. Actix can be configured to require and verify client certificates, but if application logic depends on the results of that verification across asynchronous steps or shared state, an attacker can exploit timing to reach an inconsistent or unverified state.

Consider a scenario where an endpoint first checks a lightweight cache or a fast in-memory flag (e.g., ‘session initialized’) before performing full mTLS verification or authorization. An attacker can send many concurrent requests; some may observe the flag before it is set, while others proceed after verification. This creates a race where unauthorized access or privilege changes occur because the check and the state update are not atomic with respect to mTLS authentication. The race is not in the TLS handshake itself, which is atomic at the transport layer, but in the ordering of checks and state changes in user code after the handshake.

Another specific pattern involves certificate revocation checks (CRL/OCSP). Performing revocation checks asynchronously or caching results with a stale window can lead to a race where a revoked certificate is briefly accepted between cache updates. If authorization decisions are made before the revocation check completes, an attacker can exploit the window to act with a credential that should be invalid. This is especially relevant when application-level tokens or session claims are issued or refreshed based on certificate identity without ensuring revocation is confirmed first.

Input handling and resource creation can also be implicated when mTLS identity is used to derive parameters such as file paths or database keys. If identity-based derivation occurs concurrently and is not synchronized, two connections with different valid certificates might trigger conflicting writes or reads, leading to data corruption or information leakage across identities. This becomes a security concern when identity influences access-controlled resources and the checks are split across async boundaries or background tasks.

To detect such issues, middleBrick’s 12 checks run in parallel, including Authentication and BOLA/IDOR analyses, which can surface inconsistencies in how mTLS identity is used for authorization. The scanner correlates findings with the OpenAPI spec, highlighting endpoints where authentication and authorization logic may be split or non-atomic, and provides prioritized findings with severity and remediation guidance.

Mutual Tls-Specific Remediation in Actix — concrete code fixes

Remediation focuses on ensuring mTLS verification is complete and atomic before any authorization or state changes, and avoiding races between checks and state updates. Below are concrete Actix examples that demonstrate secure patterns.

1. Enforce mTLS verification before business logic and make authorization decisions based on verified identity in a single, synchronous flow:

use actix_web::{web, App, HttpServer, Responder, HttpRequest};
use actix_web::http::header::HeaderValue;
use std::sync::{Arc, Mutex};

struct AppState {
    // Example: a thread-safe map of subject to permissions, populated after verification
    permissions: Mutex>>,
}

async fn secure_endpoint(
    req: HttpRequest,
    data: web::Data>,
) -> impl Responder {
    // Extract client certificate from request extensions (configured by mTLS)
    let cert_subject = req.extensions()
        .get::()
        .expect("mTLS verification must be enforced upstream");

    // Perform authorization based on verified identity, no additional async race window
    let perms = data.permissions.lock().unwrap();
    match perms.get(cert_subject).and_then(|p| p.first()) {
        Some(action) => format!("Action: {}", action),
        None => "Access denied".to_string(),
    }
}

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    let app_state = Arc::new(AppState {
        permissions: Mutex::new(std::collections::HashMap::new()),
    });

    HttpServer::new(move || {
        App::new()
            .app_data(web::Data::new(app_state.clone()))
            // Configure TLS with client verification; certificate subject is placed in extensions
            .route("/secure", web::get().to(secure_endpoint))
    })
    .bind_rustls(
        "127.0.0.1:8443",
        rustls::ServerConfig::builder()
            .with_safe_defaults()
            .with_client_cert_verifier(Arc::new(ClientVerifier::new()))
            .map_err(|_| std::io::Error::new(std::io::ErrorKind::Other, "TLS setup failed"))?
    )?
    .run()
    .await
}

struct ClientVerifier {
    // Placeholder for a proper verifier implementation
    accepted: std::collections::HashSet,
}

impl ClientVerifier {
    fn new() -> Self {
        Self {
            accepted: ["CN=alice".to_string(), "CN=bob".to_string()].iter().cloned().collect(),
        }
    }
}

impl rustls::server::ClientCertVerifier for ClientVerifier {
    fn client_auth_mandatory(&self) -> bool { true }
    fn verify_client_cert(&self, end_entity: &rustls::Certificate, intermediates: &[rustls::Certificate]) -> Result {
        // Simplified: in practice, validate chain, revocation, and extract subject
        let subject = format!("CN=alice"); // extract from end_entity
        if self.accepted.contains(&subject) {
            Ok(rustls::server::ClientCertVerified::assertion())
        } else {
            Err(rustls::Error::from(std::io::Error::new(std::io::ErrorKind::PermissionDenied, "bad cert")))
        }
    }
    fn validate(&self, _end_entity: &rustls::Certificate, _intermediates: &[rustls::Certificate]) -> Result {
        Ok(rustls::CertificateValid::assertion())
    }
}

2. Ensure revocation checks complete before issuing tokens or session cookies, and synchronize access to revocation state to avoid stale-window races:


use std::sync::Arc;
use tokio::sync::RwLock;

async fn check_revocation(serial: &str, revocation_db: Arc>>) -> bool {
    let db = revocation_db.read().await;
    !db.contains(serial)
}

async fn issue_token_if_valid(cert_subject: String, revocation_db: Arc>>) -> Option {
    // Perform revocation check atomically with respect to DB updates
    if check_revocation(&cert_subject, revocation_db.clone()).await {
        Some(format!("token-for-{}", cert_subject))
    } else {
        None
    }
}

3. Use atomic initialization patterns to avoid reading shared flags before they are set under mTLS identity:


use std::sync::{Arc, OnceLock};

static INITIALIZED: OnceLock = OnceLock::new();

fn set_initialized_for_subject(subject: String) {
    INITIALIZED.set(subject).expect("init once");
}

fn is_initialized_for_current(req_subject: &str) -> bool {
    INITIALIZED.get().map_or(false, |s| s == req_subject)
}

These examples emphasize verifying mTLS synchronously, performing authorization immediately after verification, and synchronizing access to shared state to eliminate timing windows. middleBrick’s scans can highlight endpoints where authorization is separated from verified identity or where revocation checks are not enforced before access, enabling you to align implementation with these patterns.

Frequently Asked Questions

Can a race condition occur even when mTLS is properly configured in Actix?
Yes, if application-level checks and state updates are not atomic with respect to verified identity, or if revocation checks are asynchronous or cached with a stale window, a race condition can still exist despite correct mTLS setup.
How does middleBrick help identify race conditions related to mTLS?
By running Authentication, BOLA/IDOR, and Authorization checks in parallel and correlating findings with the OpenAPI spec, middleBrick surfaces timing and authorization inconsistencies, providing prioritized findings and remediation guidance specific to mTLS workflows.