Privilege Escalation in Actix with Mutual Tls
Privilege Escalation in Actix with Mutual Tls — how this specific combination creates or exposes the vulnerability
In an Actix web service, mutual Transport Layer Security (mTLS) requires both the client and the server to present valid certificates. When mTLS is enabled, the server authenticates the client identity using the client certificate chain, but it does not automatically enforce authorization on what that identity is allowed to do. Privilege escalation occurs when the authorization logic does not validate the mapped identity (for example, the subject or a custom certificate attribute) against role-based or scope-based permissions before processing the request.
A common pattern is to extract the certificate subject or a username claim from the TLS peer certificate and use it to look up a user or API client. If this lookup or the subsequent permission check is incomplete—such as defaulting to a higher-privilege role when a claim is missing, or failing to validate group memberships from certificate extensions—an attacker who possesses a low-privilege certificate may gain elevated access.
For example, consider an endpoint that performs sensitive operations such as modifying administrative settings or accessing another user’s data. If the Actix handler trusts the certificate’s identity but does not verify that the associated account has the required scope or role for that operation, the attacker can effectively escalate privileges without needing to compromise the server or break TLS.
Additionally, misconfigured certificate verification settings can inadvertently weaken the authorization boundary. If the server accepts any certificate signed by a trusted CA without enforcing extended key usage or checking certificate validity windows, an attacker might use an old or overly permissive certificate to reach endpoints that should be restricted. Inconsistent validation across services in a chain—where one service enforces mTLS and another behind it does not—can also create a path for escalation through the less-protected component.
These risks map directly to the BOLA/IDOR and Privilege Escalation checks in middleBrick’s security scan. When scanning an Actix endpoint protected by mTLS, middleBrick tests unauthenticated and low-privilege certificate scenarios to detect missing authorization checks, default privilege assignments, or improper validation of certificate-derived identities. This helps surface whether the combination of mTLS and application-level permissions is correctly limiting access.
Mutual Tls-Specific Remediation in Actix — concrete code fixes
To securely handle mTLS in Actix, combine strict certificate verification with explicit authorization checks for the identity extracted from the certificate. Below are concrete, realistic code examples that demonstrate how to implement this in Rust using actix-web and rustls.
1. Configure server with client certificate verification
Set up your Actix server to require and verify client certificates. This ensures only clients with valid certificates can reach your handlers.
use actix_web::{web, App, HttpServer, Responder};
use actix_web_httpauth::extractors::AuthenticationError;
use std::sync::Arc;
use rustls::{Certificate, PrivateKey, ServerConfig};
use rustls_pemfile::{certs, pkcs8_private_keys};
use std::io::BufReader;
use std::fs::File;
async fn index() -> impl Responder {
"Hello, mTLS-protected world!"
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
// Load server certificate and private key
let cert_file = &mut BufReader::new(File::open("server-cert.pem").unwrap());
let key_file = &mut BufReader::new(File::open("server-key.pem").unwrap());
let cert_chain = certs(cert_file).unwrap().into_iter().map(Certificate).collect();
let mut keys = pkcs8_private_keys(key_file).unwrap();
let private_key = PrivateKey(keys.remove(0));
// Configure TLS with client authentication required
let mut server_config = ServerConfig::builder()
.with_safe_defaults()
.with_no_client_auth()
.with_single_cert(cert_chain, private_key)
.expect("invalid certificate or key");
// Require and verify client certificates
server_config.client_auth_mandatory = true;
server_config.client_auth_root_subjects = Arc::new(|_end_entity: &Certificate, intermediates: &[Certificate]| {
// You can implement custom verification, e.g., check subject or SANs
// Return an error if the client is not trusted
Ok(())
});
HttpServer::new(move || {
App::new()
.wrap(actix_web_httpauth::middleware::HttpAuthentication::basic(|_req, credentials| async move {
// Note: mTLS identity is available via request extensions; this is a fallback example
async { Err(AuthenticationError::from("Use mTLS identity")) }
}))
.route("/", web::get().to(index))
})
.bind_rustls("127.0.0.1:8443", server_config)?
.run()
.await
}
2. Extract identity from certificate and enforce authorization
In your handler, retrieve the verified client certificate and map it to an identity. Then enforce role or scope checks before performing sensitive actions.
use actix_web::{web, HttpRequest, HttpResponse};
use openssl::x509::X509;
// Helper to extract subject common name from the client certificate
fn extract_subject_from_cert(cert: &X509) -> Option {
cert.subject_name()
.entries()
.find_map(|e| {
if e.object().to_string() == "2.5.4.3" { // commonName OID
e.data().as_utf8_string().ok().map(|s| s.to_string())
} else {
None
}
})
}
async fn admin_action(req: HttpRequest) -> HttpResponse {
// Retrieve peer certificates from request extensions (set by a custom guard or middleware)
let certs = match req.extensions().get::>() {
Some(c) => c,
None => return HttpResponse::forbidden().body("No client certificate"),
};
let client_cert = match certs.first() {
Some(c) => c,
None => return HttpResponse::forbidden().body("No client certificate"),
};
let subject = match extract_subject_from_cert(client_cert) {
Some(s) => s,
None => return HttpResponse::forbidden().body("Invalid certificate subject"),
};
// Map subject to roles/scopes — this is where authorization happens
let roles = get_roles_for_subject(&subject);
if !roles.contains(&"admin".to_string()) {
return HttpResponse::forbidden().body("Insufficient privileges");
}
HttpResponse::ok().body(format!("Admin action allowed for subject: {}", subject))
}
// Example authorization mapping (replace with a proper lookup)
fn get_roles_for_subject(subject: &str) -> Vec {
match subject {
"alice" => vec!["admin".to_string(), "user".to_string()],
"bob" => vec!["user".to_string()],
_ => vec![],
}
}
3. Middleware to validate mTLS identity and attach authorization context
Use a guard or middleware to ensure the certificate is verified and to attach authorization context to the request, keeping handlers clean and consistent.
use actix_web::{dev::ServiceRequest, Error, middleware::Next};
use actix_web::body::BoxBody;
use actix_web::http::StatusCode;
use actix_web::middleware::from_fn;
use openssl::x509::X509;
async fn mtls_auth_middleware(
req: ServiceRequest,
next: Next,
) -> Result, Error> {
// In a real setup, the TLS client certs are provided by the server/Rustls layer
// and made available via request extensions by the Actix server configuration.
let certs = match req.extensions().get::>() {
Some(c) => c,
None => {
let res = req.into_response(
HttpResponse::build(StatusCode::FORBIDDEN)
.body("Client certificate required")
.into_body()
);
return Err(actix_web::error::ErrorForbidden("Missing client certificate"));
}
};
let subject = extract_subject_from_cert(certs.first().unwrap()).unwrap_or_default();
let roles = get_roles_for_subject(&subject);
// Attach identity and roles to request extensions for downstream handlers
req.extensions_mut().insert(subject);
req.extensions_mut().insert(roles);
next.call(req).await
}
// Apply middleware to routes
async fn protected_route() -> impl Responder {
"Access granted for authorized mTLS identity"
}
By requiring mTLS and then explicitly checking authorization based on the extracted identity, you avoid privilege escalation between authenticated and authorized boundaries. middleBrick’s scans can validate that your endpoints require proper authorization even when mTLS is in use.