GuardAPI Logo
GuardAPI

Fix SSRF (Server Side Request Forgery) in Axum

SSRF in the Axum/Rust ecosystem typically arises when the `reqwest` crate is used to fetch user-provided URLs without strict validation. In a cloud-native environment, this allows attackers to hit the EC2 metadata service (169.254.169.254), internal Kubernetes services, or loopback interfaces. To secure Axum, you must implement a multi-layered defense: protocol whitelisting, DNS resolution filtering, and IP range blacklisting.

The Vulnerable Pattern

use axum::{extract::Query, response::IntoResponse, routing::get, Router};
use serde::Deserialize;

#[derive(Deserialize)] struct ProxyParams { url: String }

// VULNERABLE: Directly fetching a user-supplied URL async fn proxy_handler(Query(params): Query) -> impl IntoResponse { let response = reqwest::get(&params.url) .await .expect(“Failed to fetch”) .text() .await .expect(“Failed to read body”); response }

The Secure Implementation

The secure implementation mitigates SSRF through three critical steps. First, it enforces the 'https' scheme to prevent protocol smuggling (e.g., file:// or gopher://). Second, it performs manual DNS resolution via 'tokio::net::lookup_host' and checks the resulting IP addresses against a blacklist of private/internal ranges (RFC 1918). This prevents the application from reaching internal metadata endpoints or local services. Third, it disables automatic redirects in the 'reqwest' client to prevent 'Time-of-Check to Time-of-Use' (TOCTOU) exploits where a malicious server redirects a validated request back to an internal IP.

use ax_um::{extract::Query, response::IntoResponse, http::StatusCode};
use url::Url;
use std::net::IpAddr;

async fn secure_proxy(Query(params): Query) -> Result<String, StatusCode> { let target_url = Url::parse(&params.url).map_err(|_| StatusCode::BAD_REQUEST)?;

// 1. Scheme Whitelisting
if target_url.scheme() != "https" {
    return Err(StatusCode::FORBIDDEN);
}

// 2. DNS Resolution and IP Validation (Anti-SSRF)
let host = target_url.host_str().ok_or(StatusCode::BAD_REQUEST)?;
let addrs = tokio::net::lookup_host(format!("{}:443", host))
    .await
    .map_err(|_| StatusCode::BAD_REQUEST)?;

for addr in addrs {
    let ip = addr.ip();
    if is_internal_ip(ip) {
        return Err(StatusCode::FORBIDDEN);
    }
}

// 3. Hardened Client
let client = reqwest::Client::builder()
    .timeout(std::time::Duration::from_secs(5))
    .redirect(reqwest::redirect::Policy::none())
    .build()
    .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;

let res = client.get(target_url.as_str()).send().await.map_err(|_| StatusCode::BAD_GATEWAY)?;
Ok(res.text().await.unwrap_or_default())

}

fn is_internal_ip(ip: IpAddr) -> bool { match ip { IpAddr::V4(v4) => v4.is_private() || v4.is_loopback() || v4.is_link_local() || v4.is_broadcast(), IpAddr::V6(v6) => v6.is_loopback() || v6.is_unspecified(), } }

System Alert • ID: 9081
Target: Axum API
Potential Vulnerability

Your Axum API might be exposed to SSRF (Server Side Request Forgery)

74% of Axum apps fail this check. Hackers use automated scanners to find this specific flaw. Check your codebase before they do.

RUN FREE SECURITY DIAGNOSTIC
GuardLabs Engine: ONLINE

Free Tier • No Credit Card • Instant Report

Verified by Ghost Labs Security Team

This content is continuously validated by our automated security engine and reviewed by our research team. Ghost Labs analyzes over 500+ vulnerability patterns across 40+ frameworks to provide up-to-date remediation strategies.