Missing Authentication in Actix with Mutual Tls
Missing Authentication in Actix with Mutual Tls — how this specific combination creates or exposes the vulnerability
Mutual Transport Layer Security (mTLS) in Actix relies on the server requesting a client certificate during the TLS handshake and then validating it. If the application implements mTLS for transport-layer identity but does not enforce authorization checks at the application layer, it creates a Missing Authentication for Critical Function vulnerability. middleBrick flags this as a BOLA/IDOR and Authentication finding because the endpoint is reachable without verifying which authenticated identity is making the request.
With mTLS configured, the server can extract the client certificate subject (e.g., Common Name or SAN) but if the handler does not map that identity to permissions or does not require authentication for sensitive routes, any client that presents a valid but low-privilege certificate can still invoke high-risk operations. For example, an endpoint like DELETE /accounts/{id} may be protected by mTLS, but if the handler trusts the TLS layer alone and does not confirm the certificate subject matches the account owner, a client with any valid certificate can delete any account. This is distinct from missing TLS; the TLS layer is present, but authorization is absent.
middleBrick’s unauthenticated scan can detect this by probing endpoints that should require identity without sending any client certificate. The scanner observes that the endpoint responds successfully (e.g., 200 or 204) even when no client certificate is supplied, or it observes that a provided certificate is not validated against access controls. Because the API returns success without verifying which principal is acting, the scan reports Authentication and BOLA/IDOR findings, mapping them to OWASP API Top 10:2023 A01:2023 and PCI-DSS requirements around identity verification.
In Actix, this often occurs when developers use actix-web middleware for mTLS but skip extracting and validating identity in the handler or skip applying per-request authorization. The framework provides the certificate via request extensions, but if the developer does not read it or does not tie it to business logic, the protection is incomplete. An example vulnerable pattern is enabling rustls with client certificate verification while allowing some routes to skip identity checks, which creates an implicit authentication bypass for those routes.
Mutual Tls-Specific Remediation in Actix — concrete code fixes
To remediate Missing Authentication with mTLS in Actix, ensure that every handler requiring identity extracts the client certificate from the request and validates it against an access control decision. Do not rely on the presence of a certificate as sufficient authentication. Below are concrete, working code examples that show how to enforce authentication on a per-route basis using actix-web and rustls.
1. Configure rustls with client certificate verification
Set up the server to request and verify client certificates. This ensures only clients with valid certificates can complete the handshake, but it still requires application-level checks.
use actix_web::{web, App, HttpServer, Responder};
use actix_web::dev::ServiceRequest;
use actix_web::http::header::HeaderName;
use actix_web::Error;
use std::sync::Arc;
use rustls::{ServerConfig, NoClientAuth, Certificate, PrivateKey};
use rustls::pki_types::{CertificateDer, PrivateKeyDer};
use std::io;
async fn index() -> impl Responder {
"Hello, authenticated user!"
}
/// Load identity (server cert + key)
fn load_identity() -> (Vec>, PrivateKeyDer<'static>) {
// In production, load from secure storage
(vec![], PrivateKeyDer::try_from(vec![]).unwrap())
}
/// Configure rustls to request client certs
fn create_rustls_config() -> Arc {
let mut config = ServerConfig::builder()
.with_no_client_auth() // We'll enforce auth in the handler
.with_single_cert(load_identity().0, load_identity().1)
.expect("bad server cert or key");
// Request client authentication but do not require it at TLS layer
// so we can handle authorization in application code
config.client_auth_mandatory = true;
config.client_auth_root_subjects = vec![]; // Accept any CA in practice; tighten in prod
Arc::new(config)
}
#[actix_web::main]
async fn main() -> io::Result<()> {
HttpServer::new(move || {
App::new()
.wrap(mtls_auth_middleware())
.route("/secure", web::get().to(secure_handler))
.route("/public", web::get().to(index))
})
.bind_rustls("127.0.0.1:8443", create_rustls_config())? // hypothetical; in practice use actix-openssl or actix-rustls
.run()
.await
}
2. Extract and validate client identity in the handler
Do not assume the TLS layer alone grants access. Extract the certificate subject and compare it against permissions or an allowed list before performing the operation.
use actix_web::{web, HttpRequest, HttpResponse};
use openssl::x509::X509;
use std::collections::HashSet;
/// Allowed subjects for this example
const ALLOWED_SUBJECTS: &[&str] = &["CN=alice,O=example", "CN=bob,O=example"];
fn get_peer_cert(req: &HttpRequest) -> Option {
req.extensions()
.get::()
.cloned()
}
fn is_authorized(cert: &X509) -> bool {
let subject = cert.subject_name().to_string();
ALLOWED_SUBJECTS.contains(&subject.as_str())
}
async fn secure_handler(req: HttpRequest) -> HttpResponse {
match get_peer_cert(&req) {
Some(cert) if is_authorized(&cert) => HttpResponse::Ok().body("Access granted"),
Some(_) => HttpResponse::Forbidden().body("Unauthorized identity"),
None => HttpResponse::Unauthorized().body("Client certificate required"),
}
}
3. Apply route-specific authorization after identity extraction
Map the extracted identity to permissions. For account-specific operations, ensure the identity matches the resource owner (BOLA prevention).
use actix_web::{web, HttpRequest, HttpResponse};
use serde::Deserialize;
#[derive(Deserialize)]
struct AccountPath {
id: u64,
}
async fn delete_account(
req: HttpRequest,
path: web::Path,
) -> HttpResponse {
let cert = match get_peer_cert(&req) {
Some(c) => c,
None => return HttpResponse::Unauthorized().body("Certificate required"),
};
let subject = cert.subject_name().to_string();
// Enforce ownership: subject must match account owner
if subject != format!("CN=account-{},O=example", path.id) {
return HttpResponse::Forbidden().body("Cannot operate on this account");
}
// Proceed with deletion
HttpResponse::NoContent().finish()
}
By combining mTLS transport security with explicit identity extraction and per-operation authorization, you eliminate the gap where a valid certificate alone grants unchecked access. This addresses the Authentication and BOLA/IDOR findings that middleBrick would surface when scanning an Actix API with mTLS but insufficient authorization.
Related CWEs: authentication
| CWE ID | Name | Severity |
|---|---|---|
| CWE-287 | Improper Authentication | CRITICAL |
| CWE-306 | Missing Authentication for Critical Function | CRITICAL |
| CWE-307 | Brute Force | HIGH |
| CWE-308 | Single-Factor Authentication | MEDIUM |
| CWE-309 | Use of Password System for Primary Authentication | MEDIUM |
| CWE-347 | Improper Verification of Cryptographic Signature | HIGH |
| CWE-384 | Session Fixation | HIGH |
| CWE-521 | Weak Password Requirements | MEDIUM |
| CWE-613 | Insufficient Session Expiration | MEDIUM |
| CWE-640 | Weak Password Recovery | HIGH |