HIGH ssrfactix

Ssrf in Actix

How SSRF Manifests in Actix

Server-Side Request Forgery (SSRF) in Actix applications occurs when user-controlled input is used to construct HTTP requests to internal or external resources without proper validation. In Actix, this typically manifests through route parameters, query strings, or request bodies that contain URLs which are then passed to HTTP clients like reqwest, surf, or Actix's own awc client.

A common Actix SSRF pattern looks like this:

#[get("/proxy/{url}")]
async fn proxy_url(req: HttpRequest, path: web::Path<String>) -> impl Responder {
    let url = path.url.clone();
    let client = awc::Client::new();
    let resp = client.get(url).send().await?;
    HttpResponse::build(resp.status()).body(resp.body().await?)
}

This endpoint allows any URL to be proxied, exposing internal services. Attackers can target internal APIs at http://localhost:8080, cloud metadata endpoints like http://169.254.169.254 (AWS), or services behind corporate firewalls.

Actix-specific SSRF vulnerabilities often appear in:

  • Webhook handlers that accept callback URLs
  • Microservice orchestrators that call other services based on user input
  • Configuration fetchers that download files from user-specified locations
  • API aggregators that collect data from multiple sources

The danger is amplified in containerized Actix deployments where internal network services are exposed but not publicly accessible, making SSRF the primary attack vector.

Actix-Specific Detection

Detecting SSRF in Actix requires both static analysis of route handlers and dynamic testing of exposed endpoints. middleBrick's Actix-specific detection includes:

Pattern Analysis: Scanning for Actix route handlers that accept URL parameters and use them in HTTP client calls without validation. The scanner identifies patterns like:

#[get("/fetch/{url}")]
async fn fetch_url(path: web::Path<String>) -> impl Responder {
    // Vulnerable: direct use of path parameter
    let client = reqwest::Client::new();
    let resp = client.get(path.url).send().await?;
    // ...
}

Runtime Testing: middleBrick actively probes Actix endpoints with SSRF payloads including:

  • Internal IP addresses (127.0.0.1, 10.x.x.x, 172.16.x.x, 192.168.x.x)
  • Cloud metadata endpoints (169.254.169.254, 169.254.254.169)
  • Special protocols (file://, ftp://, gopher://)
  • Large payloads to test for blind SSRF

Network Boundary Testing: The scanner attempts connections to both public and private IP ranges to determine if the Actix service can reach internal resources. This reveals whether the application is properly sandboxed.

Protocol Validation: middleBrick checks if Actix endpoints validate the URL scheme, preventing attacks via non-HTTP protocols that could lead to file disclosure or other protocol-specific vulnerabilities.

For Actix applications using awc or reqwest, middleBrick specifically tests the client configuration to ensure no unsafe defaults are enabled.

Actix-Specific Remediation

Fixing SSRF in Actix requires a defense-in-depth approach using Actix's type system and Rust's safety features. Here are Actix-specific remediation patterns:

URL Validation with Actix Extractors:

use actix_web::{get, web, HttpResponse, Responder};
use url::Url;
use std::net::IpAddr;

#[derive(Debug)]
struct ValidatedUrl(Url);

impl actix_web::FromRequest for ValidatedUrl {
    type Config = ();
    type Error = actix_web::Error;
    type Future = Result<Self, Self::Error>;

    fn from_request(
        req: &HttpRequest,
        _payload: &mut actix_http::Payload,
    ) -> Self::Future {
        let path_url = web::Path::::from_request(req, _payload)?.into_inner();
        
        match Url::parse(&path_url) {
            Ok(url) => {
                // Block private IP ranges
                if is_private_ip(&url) {
                    return Err(actix_web::error::ErrorForbidden("Private IP blocked"));
                }
                Ok(ValidatedUrl(url))
            }
            Err(_) => Err(actix_web::error::ErrorBadRequest("Invalid URL"))
        }
    }
}

fn is_private_ip(url: &Url) -> bool {
    if let Some(host) = url.host_str() {
        if let Ok(ip) = host.parse::() {
            return ip.is_private() || ip.is_loopback();
        }
    }
    false
}

#[get("/safe-proxy/{url}")]
async fn safe_proxy(validated: ValidatedUrl) -> impl Responder {
    let client = awc::Client::new();
    let resp = client.get(validated.0.as_str()).send().await?;
    HttpResponse::build(resp.status()).body(resp.body().await?)
}

Allowlist Approach:

use actix_web::{get, web, HttpResponse, Responder};
use url::Url;

#[get("/fetch-content/{url}")]
async fn fetch_content(path: web::Path<String>) -> impl Responder {
    let allowlist = vec![
        "https://api.example.com",
        "https://data.example.org",
    ];
    
    match Url::parse(&path.url) {
        Ok(url) => {
            if allowlist.iter().any(|allowed| url.host_str() == Some(allowed)) {
                let client = awc::Client::new();
                let resp = client.get(url.as_str()).send().await?;
                HttpResponse::build(resp.status()).body(resp.body().await?)
            } else {
                HttpResponse::BadRequest().body("URL not in allowlist")
            }
        }
        Err(_) => HttpResponse::BadRequest().body("Invalid URL format")
    }
}

Network-Level Isolation: Deploy Actix containers with restricted network policies using Docker or Kubernetes:

# docker-compose.yml example
db:
  image: postgres
  networks:
    - internal
api:
  build: .
  networks:
    - internal
  cap_drop:
    - ALL
  cap_add:
    - NET_BIND_SERVICE
  # No public ports exposed, only internal network access

Using Safe HTTP Clients: Configure awc or reqwest with strict timeout and redirect policies:

let client = awc::ClientBuilder::new()
    .max_redirects(2)
    .connect_timeout(Duration::from_secs(5))
    .timeout(Duration::from_secs(10))
    .finish();

Related CWEs: ssrf

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

Frequently Asked Questions

How can I test my Actix application for SSRF vulnerabilities?
Use middleBrick's self-service scanner by submitting your Actix API endpoint URL. The scanner actively tests for SSRF by attempting connections to internal IP ranges, cloud metadata endpoints, and special protocols. It provides a security score with specific findings about SSRF vulnerabilities and remediation guidance.
What's the difference between SSRF in Actix vs other frameworks?
Actix SSRF vulnerabilities often stem from its async-first design where route handlers directly use HTTP clients like awc or reqwest without validation. The framework's performance-oriented approach can lead developers to skip input validation. middleBrick's Actix-specific detection identifies these patterns and provides remediation tailored to Actix's async/await patterns and type system.