Ssrf Server Side in Actix with Mutual Tls
Ssrf Server Side in Actix with Mutual Tls — how this specific combination creates or exposes the vulnerability
Server-side request forgery (SSRF) in Actix applications that also enforce mutual TLS (mTLS) can create nuanced attack surfaces. When an Actix service uses mTLS for client authentication, developers may assume that strong client certificate requirements alone prevent unauthorized outbound calls. This is not always the case for SSRF.
SSRF occurs when an attacker can induce the server to make HTTP requests to arbitrary internal or external endpoints. In an Actix service, if user-controlled data (e.g., a URL from a request body or query parameter) is passed to an HTTP client without strict allowlisting or network-level controls, the server can be tricked into making requests to sensitive internal resources. Examples include metadata services (e.g., 169.254.169.254 on cloud environments), internal dashboards, or service endpoints that are not exposed to the public internet.
With mTLS in place, the Actix server presents a client certificate when acting as an HTTP client. If the certificate has broad permissions or if the certificate and private key are inadvertently exposed to the application (for instance, through a compromised container or a misconfigured volume), an SSRF vulnerability can be leveraged to perform authenticated requests to internal services that trust the mTLS client certificate. Even when mTLS is enforced for inbound connections, the same certificate may be reused by the Actix app for outbound calls, effectively bypassing network segregation that assumes only authenticated clients can initiate connections.
The combination therefore does not remove SSRF; it shifts the concern to what the compromised server can do with its mTLS credentials. An attacker who can control request targets may cause the Actix service to perform authenticated requests to internal APIs, databases, or administrative interfaces that are protected by mTLS but not intended for arbitrary invocation. This makes input validation, network egress controls, and certificate scope as important as the TLS mode itself.
To detect such issues, scanning tools check whether user-supplied URLs are passed directly to HTTP clients, whether certificate material is handled safely, and whether outbound requests can be directed to internal endpoints. In an OpenAPI/Swagger specification, this is reflected in parameters that accept full URLs (e.g., a webhook or callback URL) without restricting schemes, hosts, or ports, combined with a lack of server-side network allowlisting.
Mutual Tls-Specific Remediation in Actix — concrete code fixes
Remediation focuses on strict input validation, network controls, and safe handling of mTLS credentials in Actix. Avoid using user-controlled data as the request target, and if outbound calls are necessary, use allowlisted endpoints and restrict TLS configuration to the minimum required scope.
Example: Safe outbound HTTP client in Actix using native-tls and reqwest with explicit certificate and hostname validation.
use actix_web::{web, HttpResponse, Result};
use reqwest::Client;
use std::fs;
use std::sync::Arc;
async fn call_allowed_service(url: web::Json) -> Result<HttpResponse> {
// Strict allowlist: only specific internal hostnames and paths are permitted
let allowed_hosts = ["internal-api.example.com"];
let parsed = url::Url::parse(&url.into_inner()).map_err(|_| actix_web::error::ErrorBadRequest("invalid URL"))?;
if !allowed_hosts.contains(&parsed.host_str().unwrap_or("")) {
return Ok(HttpResponse::BadRequest().body("target not allowed"));
}
// Load client certificate and key explicitly; keep them out of application code
let cert = fs::read("/run/secrets/client.crt").map_err(|_| actix_web::error::ErrorInternalServerError("cert read failed"))?;
let key = fs::read("/run/secrets/client.key").map_err(|_| actix_web::error::ErrorInternalServerError("key read failed"))?;
let identity = native_tls::Identity::from_pkcs8(&cert, &key).map_err(|_| actix_web::error::ErrorInternalServerError("invalid identity"))?;
// Build client with strict TLS settings and optional CA verification
let tls = native_tls::TlsConnector::builder()
.identity(identity)
.build()
.map_err(|_| actix_web::error::ErrorInternalServerError("tls build failed"))?;
let connector = tokio_native_tls::TlsConnector::from(Arc::new(tls));
let client = Client::builder()
.use_preconfigured_tls(connector)
.build()
.map_err(|_| actix_web::error::ErrorInternalServerError("client build failed"))?;
// Perform the request with a timeout and strict no-proxy settings
let response = client.get(parsed.as_str())
.timeout(std::time::Duration::from_secs(5))
.send()
.await
.map_err(|_| actix_web::error::ErrorGatewayTimeout("upstream error"))?;
let body = response.text().await.map_err(|_| actix_web::error::ErrorInternalServerError("read error"))?;
Ok(HttpResponse::Ok().body(body))
}
Key points in this example:
- Explicit host allowlist prevents SSRF to arbitrary internal or external targets.
- Client certificate and private key are read from secure paths (e.g., Kubernetes secrets) rather than being embedded or auto-discovered.
- TLS configuration is explicit, avoiding default or overly permissive trust settings that could lead to unintended trust of compromised CAs.
- Timeouts and controlled error handling reduce the window for hanging requests or information leakage through timing differences.
Additional measures include network policies that restrict egress to known service endpoints and runtime secrets management to prevent accidental exposure of mTLS credentials. Even with mTLS, scanning for SSRF patterns in OpenAPI specs and runtime behavior remains essential to ensure that user-controlled parameters cannot redirect the service to unintended locations.