Prototype Pollution in Actix with Mutual Tls
Prototype Pollution in Actix with Mutual Tls — how this specific combination creates or exposes the vulnerability
Prototype pollution in Actix applications can be exacerbated when Mutual Transport Layer Security (mTLS) is used, because mTLS enforces client certificate validation and strongly authenticates the two endpoints. This trust relationship can lead developers to assume that inbound requests are inherently safe, which may reduce vigilance around input validation and object merging logic. In practice, mTLS ensures the client is known, but it does not constrain what that authenticated client is allowed to send.
Actix-web commonly deserializes JSON payloads into Rust structs or uses dynamic representations (e.g., serde_json::Value) for flexible APIs. When user-controlled data is merged into a shared configuration or object—such as using recursive merging utilities or frameworks that apply updates to a base object—prototype pollution can occur if keys like __proto__, constructor, or prototype are allowed through. An authenticated client presenting a valid certificate can therefore inject properties that affect application-wide behavior, including default values for future deserialization or runtime trait checks.
Because mTLS often supports automated, machine-to-machine communication, an attacker who compromises a client certificate can repeatedly probe for endpoints that accept mutable JSON and apply unsafe merges. The combination of strong identity assurance and lenient object handling creates a scenario where the attack surface is effectively widened: the attacker is trusted, but the application may treat trusted input as untrusted only at the transport layer, not at the data layer. Findings from a middleBrick scan can highlight whether your Actix endpoints differentiate between transport identity and data semantics, flagging unsafe deserialization and missing property-level authorization even when mTLS is in place.
Real-world patterns that can lead to prototype pollution include using crates that implement recursive merging without filtering dangerous keys, or frameworks that apply partial updates via methods like serde_json::Map manipulation. For example, an endpoint that accepts a PATCH-style update and merges it into a shared configuration object may inadvertently allow an authenticated client to set __proto__ properties, affecting all subsequent deserialization or equality checks. middleBrick’s checks for Input Validation and Unsafe Consumption help detect whether your Actix endpoints properly sanitize keys before merging, even when mTLS is used for client authentication.
Mutual Tls-Specific Remediation in Actix — concrete code fixes
Securing Actix with mTLS while preventing prototype pollution requires both correct TLS configuration and disciplined data handling. On the TLS side, you must require and verify client certificates, and on the data side, you must avoid unsafe merges and validate all incoming keys. Below are concrete, realistic code examples that demonstrate both aspects.
1. Mutual TLS setup in Actix (rustls)
Configure the Actix server to require client certificates and validate them against a trusted CA. This ensures only authorized clients can connect, but does not by itself protect against polluted object properties.
use actix_web::{web, App, HttpServer};
use actix_web::dev::Server;
use std::sync::Arc;
use rustls::{ServerConfig, Certificate, PrivateKey};
use rustls_pemfile::{certs, pkcs8_private_keys};
use std::io::BufReader;
use std::fs::File;
async fn run_mtls_server() -> std::io::Result<Server> {
// Load server certificate and key
let mut cert_file = BufReader::new(File::open("server-cert.pem").unwrap());
let mut key_file = BufReader::new(File::open("server-key.pem").unwrap());
let cert_chain = certs(&mut cert_file).unwrap().into_iter().map(Certificate).collect();
let mut keys = pkcs8_private_keys(&mut key_file).unwrap();
let server_key = keys.remove(0);
// Configure TLS with client authentication required
let mut config = ServerConfig::builder()
.with_safe_defaults()
.with_no_client_auth(); // start without client certs
let client_auth = rustls::server::ClientAuthMode::VerifyAndRequireClientAuth(Arc::new(|_endpoints| Ok(vec![])));
config.client_auth = client_auth;
config.set_single_cert(cert_chain, server_key).expect("invalid cert or key");
// Bind Actix to the TLS acceptor
HttpServer::new(|| {
App::new()
.route("/update", web::post().to(update_handler))
})
.bind_rustls("127.0.0.1:8443", config)?
.run()
.await
}
2. Safe deserialization and property filtering to prevent prototype pollution
Ensure incoming JSON does not contain dangerous keys before merging. Use a deny-list or allow-list approach depending on your data model. For PATCH-style updates, prefer strongly-typed structs or explicitly whitelisted fields.
use actix_web::{post, web, HttpResponse};
use serde_json::{json, Map, Value};
const DANGEROUS_KEYS: &[&str] = &["__proto__", "constructor", "prototype"];
fn has_dangerous_keys(obj: &Map<String, Value>) -> bool {
obj.keys().any(|k| DANGEROUS_KEYS.contains(&k.as_str()))
}
#[post("/update")]
async fn update_handler(body: web::Json) -> HttpResponse {
let obj = body.as_object();
match obj {
Some(map) if has_dangerous_keys(map) => {
return HttpResponse::BadRequest().json(json!({
"error": "Prototype pollution attempt detected", "keys": map.keys().filter(|k| DANGEROUS_KEYS.contains(&k.as_str())).collect<Vec<&str>>()
}));
}
Some(map) => {
// Perform a safe merge: avoid recursive merge that can pollute prototypes
// Prefer explicit field-by-field updates or use a crate that filters dangerous keys
// Example: apply only known safe keys
let safe_updates: Map<String, Value> = map.iter()
.filter(|(k, _)| !DANGEROUS_KEYS.contains(&k.as_str()))
.map(|(k, v)| (k.clone(), v.clone()))
.collect();
// Apply safe_updates to your configuration or domain object here
HttpResponse::Ok().json(json!({ "applied": safe_updates }))
}
None => HttpResponse::BadRequest().json(json!({ "error": "Expected an object" })),
}
}
In this remediation, mTLS handles transport identity, while the application enforces data integrity by rejecting objects with prototype-polluting keys. Combining both reduces risk: even authenticated clients cannot abuse dangerous property names.
middleBrick scans can validate that your endpoints reject dangerous keys and that your TLS configuration enforces client certificate verification. Its findings for Authentication and Input Validation provide prioritized remediation steps tailored to your Actix implementation.