HIGH ssrfactixmutual tls

Ssrf in Actix with Mutual Tls

Ssrf in Actix with Mutual Tls — how this specific combination creates or exposes the vulnerability

Server-Side Request Forgery (SSRF) in Actix web applications using Mutual TLS (mTLS) can arise when the server acts as an HTTP client and makes outbound requests to user-supplied URLs. mTLS ensures the client presents a valid certificate, but it does not restrict where the server initiates connections. An attacker can supply a target URI that the server-side handler opens, causing the server to reach internal services that are only protected by mTLS because the server holds the client certificate or uses a trusted CA to validate outbound connections.

In an Actix service, if an endpoint accepts a URL and uses an Actix client (e.g., reqwest or Actix’s own client) to perform an outbound HTTP call without strict allowlisting or network segmentation, the mTLS client configuration can inadvertently provide the server the means to reach sensitive internal endpoints. For example, an internal admin interface or a metadata service might require mTLS client certificates; if the server uses its own mTLS credentials to make the request, it can bypass protections that would otherwise block unauthenticated external clients. This exposes functionality that is not intended to be reachable from the public API surface.

Even with mTLS on inbound connections, SSRF remains possible because the server’s outbound mTLS setup can be reused to pivot to internal endpoints. The server does not validate that the target is external or safe; it only ensures it can present a valid certificate, which it already possesses. Attack patterns such as probing internal IP ranges (e.g., 169.254.169.254, 127.0.0.1, or internal Kubernetes services) can be conducted through the compromised endpoint.

An example of an unsafe handler in Actix might look like this, where a user-supplied URL is fetched using a configured reqwest client that uses mTLS via a client certificate:

use actix_web::{web, HttpResponse, Result};
use reqwest::Client;

async fn fetch_url(client: web::Data<Client>, body: web::Json<serde_json::Value>) -> Result<HttpResponse> {
    let url = body.get("url").and_then(|v| v.as_str()).unwrap_or("");
    // Unsafe: no validation of the target URL
    let response = client.get(url).send().await?;
    let text = response.text().await?;
    Ok(HttpResponse::Ok().body(text))
}

If the client is configured with mTLS (client certificate and key), it can present credentials to internal services that accept mTLS, enabling SSRF against those internal endpoints. The vulnerability is not in mTLS itself but in the lack of URL validation and network controls around the server’s outbound requests.

middleBrick can detect SSRF by analyzing the unauthenticated attack surface and correlating runtime behavior with API specs. In OpenAPI/Swagger analysis, middleBrick cross-references defined paths and parameters with observed outbound call patterns to identify risky endpoint behaviors. This is especially relevant when mTLS is used, as the presence of client certificates may indicate higher-trust outbound calls that require stricter controls.

Mutual Tls-Specific Remediation in Actix — concrete code fixes

Remediation focuses on input validation, network controls, and limiting the scope of mTLS usage. Do not use server-side mTLS credentials to reach arbitrary user-supplied URLs. Instead, enforce strict allowlists, disable client certificate usage for outbound calls to user-controlled targets, and isolate sensitive internal endpoints behind network-level restrictions.

Below are concrete Actix code examples that implement safe patterns.

1) Validate and restrict target URLs

Only permit URLs to a predefined set of hosts or schemas. Reject private IPs, localhost, and internal metadata endpoints.

use actix_web::{web, HttpResponse, Result};
use url::Url;

fn is_safe_url(input: &str) -> bool {
    let parsed = match Url::parse(input) {
        Ok(u) => u,
        Err(_) => return false,
    };
    let host = match parsed.host_str() {
        Some(h) => h,
        None => return false,
    };
    // Allowlist only specific external domains
    ["api.example.com", "data.example.com"].contains(&host) &&
    // Ensure not localhost or private IP ranges
    !parsed.host_ip().map(|ip| ip.is_loopback() || ip.is_private()).unwrap_or(false)
}

async fn fetch_url_safe(client: web::Data<reqwest::Client>, body: web::Json<serde_json::Value>) -> Result<HttpResponse> {
    let url = body.get("url").and_then(|v| v.as_str()).unwrap_or("");
    if !is_safe_url(url) {
        return Ok(HttpResponse::BadRequest().body("Invalid target URL"));
    }
    let response = client.get(url).send().await?;
    let text = response.text().await?;
    Ok(HttpResponse::Ok().body(text))
}

2) Use a separate outbound client without mTLS for public endpoints

If your inbound API uses mTLS, configure a distinct reqwest client for outbound calls that does not include client certificates. This prevents the server from using its mTLS identity when contacting user-controlled endpoints.

use actix_web::{web, HttpResponse, Result};
use reqwest::{Client, Identity};
use std::fs;

// Build a client without client certificate for outbound calls
fn build_public_client() -> Client {
    Client::builder()
        .build()
        .expect("Failed to build public client")
}

// Build a separate mTLS client for internal trusted calls only
fn build_mtls_client(cert_path: &str, key_path: &str) -> Client {
    let cert = fs::read(cert_path).expect("unable to read cert");
    let key = fs::read(key_path).expect("unable to read key");
    let identity = Identity::from_pkcs8(&cert, &key).expect("failed to parse identity");
    Client::builder()
        .identity(identity)
        .build()
        .expect("failed to build mtls client")
}

async fn fetch_public(client: web::Data<Client>, body: web::Json<serde_json::Value>) -> Result<HttpResponse> {
    let url = body.get("url").and_then(|v| v.as_str()).unwrap_or("");
    if !is_safe_url(url) {
        return Ok(HttpResponse::BadRequest().body("Invalid target URL"));
    }
    let response = client.get(url).send().await?;
    let text = response.text().await?;
    Ok(HttpResponse::Ok().body(text))
}

3) Enforce network-level isolation

Ensure that services requiring mTLS are not reachable from the public API container. Use network policies, firewalls, or service mesh rules to prevent the API host from initiating connections to internal mTLS-protected endpoints. This complements application-level controls and reduces reliance on runtime validation alone.

middleBrick supports compliance mappings that highlight SSRF risks under frameworks such as OWASP API Top 10 and SOC2. In environments using mTLS, it’s important to document and test whether outbound calls inadvertently leverage privileged identities. The Pro plan enables continuous monitoring to detect changes in endpoint behavior that could reintroduce SSRF.

Related CWEs: ssrf

CWE IDNameSeverity
CWE-918Server-Side Request Forgery (SSRF) CRITICAL
CWE-441Unintended Proxy or Intermediary (Confused Deputy) HIGH

Frequently Asked Questions

Does mTLS prevent SSRF in Actix APIs?
No. mTLS secures inbound client-to-server authentication but does not restrict which outbound connections a server can make. SSRF can occur if the server uses its mTLS credentials to reach user-controlled URLs, especially internal services that trust the server’s client certificate.
How can I safely call external APIs from an Actix service that uses mTLS?
Use a separate outbound HTTP client that does not include client certificates, validate and allowlist target URLs, and enforce network segmentation so that mTLS-protected endpoints are not reachable from the public API path. Tools like middleBrick can help identify risky outbound call patterns during scans.