Information Disclosure in Actix with Mutual Tls
Information Disclosure in Actix with Mutual Tls
Mutual Transport Layer Security (mTLS) in Actix is designed to authenticate both client and server using X.509 certificates. When configured correctly, mTLS ensures that only clients with a trusted certificate can establish a connection. However, misconfigurations can lead to information disclosure, where sensitive data such as certificate details, internal hostnames, or stack traces are exposed to unauthenticated or improperly authenticated clients.
One common issue arises when the server does not properly validate client certificates, allowing connections from clients that present invalid or self-signed certificates. In such cases, Actix may still establish a connection, potentially logging or echoing certificate information in error messages or debug output. An attacker could leverage this behavior to enumerate trusted certificate authorities or gather details about the server’s identity by sending intentionally malformed or unauthorized TLS handshake requests.
Another risk involves the exposure of internal service metadata through HTTP headers or application responses when mTLS is not strictly enforced. For example, if an Actix service falls back to an unencrypted or less secure communication path due to a failed certificate validation, it might inadvertently reveal internal hostnames, API endpoints, or version information in plaintext. This can aid an attacker in mapping the infrastructure or crafting more targeted attacks.
Additionally, improper handling of certificate verification errors can lead to verbose logging or exception messages that disclose sensitive paths, file locations, or cryptographic parameters. These logs may be accessible to unauthorized users if log aggregation or monitoring systems are not properly secured. An example of a vulnerable Actix configuration might include enabling detailed error logs without filtering sensitive data, which could expose stack traces containing file system paths or internal network details during TLS handshake failures.
To illustrate a typical mTLS setup in Actix that may inadvertently expose information, consider the following Rust code snippet. While the code enforces client certificate verification, insufficient error handling can still lead to information leakage through debug output:
use actix_web::{web, App, HttpServer, Responder};
use actix_web::middleware::Logger;
use actix_web::http::header::CONTENT_TYPE;
use openssl::ssl::{SslAcceptor, SslFiletype, SslMethod};
fn create_ssl_config() -> SslAcceptor {
let mut builder = SslAcceptor::mozilla_intermediate(SslMethod::tls()).unwrap();
builder.set_private_key_file("key.pem", SslFiletype::PEM).unwrap();
builder.set_certificate_chain_file("cert.pem").unwrap();
builder.set_client_ca_list_file("ca.pem").unwrap();
builder.set_verify(openssl::ssl::SslVerifyMode::PEER | openssl::ssl::SslVerifyMode::FAIL_IF_NO_PEER_CERT, verify_callback);
builder.build()
}
fn verify_callback(verified: bool, ctx: &mut openssl::ssl::VerifyContext) -> bool {
if !verified {
// Risk: verbose logging may expose certificate details
eprintln!("Certificate verification failed: {:?}", ctx.error());
}
verified
}
async fn index() -> impl Responder {
"Hello, secure world!"
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
let ssl = create_ssl_config();
HttpServer::new(|| {
App::new()
.wrap(Logger::default())
.route("/", web::get().to(index))
})
.bind_openssl("127.0.0.1:8080", ssl)?
.run()
.await
}
In this example, the verify_callback function logs the error from the verification context if the client certificate is not valid. While useful for debugging, this logging can expose internal error details to an attacker who triggers the failure. A more secure approach is to handle verification failures silently or with generic messages, avoiding any disclosure of certificate or error details.
Furthermore, if the Actix server is behind a load balancer or reverse proxy that terminates TLS, misconfigured headers or forwarded protocols can cause the application to believe the connection is not secure, potentially leading to mixed-content responses or insecure fallback behavior. This can result in sensitive data being transmitted or rendered in an unencrypted context, increasing the risk of interception or manipulation.
Mutual Tls-Specific Remediation in Actix
Remediation focuses on strict certificate validation, minimizing exposed information, and ensuring consistent secure handling across the Actix application. The following code examples demonstrate secure configurations that reduce the risk of information disclosure.
First, ensure that client certificate verification is enforced and that error handling does not leak details. Replace verbose logging with generic failure handling:
use actix_web::{web, App, HttpServer, Responder};
use openssl::ssl::{SslAcceptor, SslFiletype, SslMethod, SslVerifyMode};
fn create_secure_ssl_config() -> SslAcceptor {
let mut builder = SslAcceptor::mozilla_intermediate(SslMethod::tls()).unwrap();
builder.set_private_key_file("key.pem", SslFiletype::PEM).unwrap();
builder.set_certificate_chain_file("cert.pem").unwrap();
builder.set_client_ca_list_file("ca.pem").unwrap();
builder.set_verify(
SslVerifyMode::PEER | SslVerifyMode::FAIL_IF_NO_PEER_CERT,
|_verified, _ctx| {
// Fail silently without logging certificate details
false
},
);
builder.build()
}
async fn index() -> impl Responder {
"Hello, secure world!"
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
let ssl = create_secure_ssl_config();
HttpServer::new(|| {
App::new()
.route("/", web::get().to(index))
})
.bind_openssl("127.0.0.1:8080", ssl)?
.run()
.await
}
This configuration disables detailed error logging during certificate verification, preventing exposure of certificate-related information. The callback returns false for any verification failure, causing the connection to be rejected without revealing the reason.
Second, ensure that the server does not fall back to insecure communication paths. When behind a proxy, explicitly configure trusted headers and protocol settings to prevent misinterpretation of the connection security. For example, set X-Forwarded-Proto validation if applicable:
use actix_web::middleware::Condition;
use actix_web::dev::ServiceRequest;
use actix_web::error::ErrorForbidden;
use std::future::{ready, Ready};
fn validate_secure_headers(req: ServiceRequest) -> Result {
let headers = req.headers();
if headers.get("X-Forwarded-Proto").and_then(|v| v.to_str().ok()) != Some("https") {
return Err((req, ErrorForbidden("Insecure request")));
}
Ok(req)
}
// Integrate this validator in your Actix pipeline conditionally
This ensures that only requests with the correct forwarded protocol are accepted, reducing the risk of insecure fallback. Finally, regularly rotate certificates and use certificate transparency logs to detect unauthorized certificates issued for your domains, mitigating the impact of potential certificate compromise.