Man In The Middle in Actix with Mutual Tls
Man In The Middle in Actix with Mutual Tls — how this specific combination creates or exposes the vulnerability
In an Actix-based service, enabling mutual TLS (mTLS) means both the server and the client present certificates during the TLS handshake. When mTLS is configured but the application does not validate client certificates properly, a Man In The Middle (MitM) risk can persist in the deployment or operational layer, even though the protocol itself is strong.
For example, if the server is configured to request client certificates but does not enforce verification (e.g., due to an incomplete Rust TLS builder configuration or missing certificate chain validation), an attacker on the network can relay traffic through an unverified client. In an Actix web service, this could occur if the TLS acceptor is set to request client certs but skips verification, allowing an attacker positioned as a proxy to present any certificate and be accepted as a legitimate client.
Another scenario specific to Actix arises when the service terminates TLS at a load balancer or ingress and forwards traffic internally over HTTP to the Actix application. In this architecture, the mTLS protection is applied only at the edge. An attacker who can reach the internal network segment can intercept unencrypted traffic between the edge and the Actix service, effectively acting as a MitM despite mTLS at the perimeter. This shows that mTLS in Actix only protects the connection to the edge; without end-to-end mTLS all the way to the application, the in-transit protections are broken.
Additionally, implementation issues in Actix can weaken mTLS. If the application loads a CA bundle but does not include intermediate certificates, clients with certificates signed by those intermediates may be accepted without proper validation. In Rust Actix configurations, failing to set the correct certificate verification depth or not checking the client certificate’s hostname/subject against an allowlist can lead to an attacker using a valid but unintended certificate to perform a MitM.
Operational exposures also matter. If private keys for server or client certificates are stored insecurely or if certificate revocation is not enforced, an attacker who obtains a valid certificate can impersonate a client or server and conduct a MitM against the Actix service. Therefore, mTLS in Actix must include strict verification, complete certificate chains, and secure key management to avoid creating or exposing MitM paths.
Mutual Tls-Specific Remediation in Actix — concrete code fixes
To securely implement mutual TLS in Actix and prevent MitM issues, enforce client certificate verification and protect the certificate chain. Below are concrete, working Rust examples using native-tls and actix-web-native-tls (or actix-http with tokio-native-tls depending on your runtime). These examples assume you have PEM-encoded server and CA certificates.
1) Server-side mTLS with strict client verification using native-tls and actix-web-native-tls:
use actix_web::{web, App, HttpServer}; use actix_web_native_tls::TlsAcceptor; use native_tls::{Identity, TlsAcceptor as NativeTlsAcceptor};
use std::fs; use std::sync::Arc;
#[actix_web::main]
async fn main() -> std::io::Result<()> {
// Load server identity (cert + private key)
let cert = fs::read("server-cert.pem").expect("unable to read server cert");
let key = fs::read("server-key.pem").expect("unable to read server key");
let identity = Identity::from_pkcs8(&cert, &key).expect("invalid server identity");
// Configure server TLS acceptor with client CA and verification
let mut builder = native_tls::TlsAcceptor::builder(identity);
let ca_cert = fs::read("ca-cert.pem").expect("unable to read CA cert");
let ca = native_tls::Certificate::from_pem(&ca_cert).expect("invalid CA cert");
builder.add_root_certificate(ca);
builder.client_auth_native_tls(native_tls::ClientAuthMode::Verify);
// Require and verify client certificates against the CA
builder.min_protocol_version(Some(native_tls::Protocol::Tlsv12));
let acceptor = TlsAcceptor::from(Arc::new(builder.build().expect("failed to build TLS acceptor")));
HttpServer::new(move || {
App::new()
.wrap(TlsAcceptor::from(acceptor.clone()))
.route("/health", web::get().to(|| async { "ok" }))
})
.bind_rustls("127.0.0.1:8443", acceptor)?
.run()
.await
}
Notes:
- Use
client_auth_native_tls(native_tls::ClientAuthMode::Verify)to enforce client certificate validation against the provided CA. This prevents unverified clients from being accepted, reducing MitM risk. - Always set minimum protocol versions (e.g., TLSv1.2) to avoid downgrade attacks.
- Ensure the CA certificate includes necessary intermediates; otherwise, valid client certs may be rejected or improperly validated.
2) Actix with tokio-native-tls (alternative runtime) showing certificate verification and error handling:
use actix_web::{web, App, HttpServer}; use actix_web_httpauth::extractors::bearer::BearerAuth; use actix_http::ConnectInfo; use tokio_native_tls::native_tls::{TlsAcceptor as NativeTlsAcceptor, Identity, Certificate};
use tokio_native_tls::TlsAcceptor;
use std::net::SocketAddr; use std::sync::Arc; use tokio::net::TcpListener;
#[actix_web::main]
async fn main() -> std::io::Result<()> {
let server_identity = Identity::from_pkcs8(
&fs::read("server-cert.pem").expect("cannot read server cert"),
&fs::read("server-key.pem").expect("cannot read server key"),
).expect("invalid server identity");
let mut builder = NativeTlsAcceptor::builder(server_identity);
let ca = Certificate::from_pem(&fs::read("ca-cert.pem").expect("cannot read CA"))?;
builder.add_root_certificate(ca);
builder.client_auth(native_tls::ClientAuth::Required);
let acceptor = TlsAcceptor::from(Arc::new(builder.build()?));
let listener = TcpListener::bind("127.0.0.1:8443").await?;
HttpServer::new(move || {
App::new()
.wrap(TlsAcceptor::from(acceptor.clone()))
.route("/api/secure", web::get().to(|| async { "secure" }))
})
.listen_rustls(listener, acceptor)?
.run()
.await
}
Remediation checklist to prevent MitM with mTLS in Actix:
- Enforce client certificate verification (
VerifyorRequired) and never useNoneor permissive modes in production. - Provide a complete CA bundle that includes intermediates to avoid validation bypasses.
- Use strong minimum protocol versions and cipher suites; disable weak ciphers and TLS 1.0/1.1.
- Do not terminate mTLS at an edge and forward unencrypted internally; if internal forwarding is required, continue mTLS end-to-end or use additional authentication between services.
- Protect private keys and automate certificate rotation; monitor revocation via CRL or OCSP where feasible.