GuardAPI Logo
GuardAPI
Automated Security Protocol

How to fix SSRF (Server Side Request Forgery)
in Salvo

Executive Summary

SSRF in Rust's Salvo framework is a critical sink where an attacker forces the server to make outbound requests to internal metadata services (IMDS), local loopback, or private subnets. If you're piping raw user input into reqwest or hyper without validation, you're handing over a proxy to your internal infrastructure. This bypasses firewalls and exposes internal-only APIs.

The Vulnerable Pattern

VULNERABLE CODE
use salvo::prelude::*;

#[handler] async fn proxy(req: &mut Request, res: &mut Response) { let url = req.query::(“target”).unwrap_or_default();

// VULNERABLE: Directly fetching a user-provided URL
// An attacker can pass 'http://169.254.169.254/latest/meta-data/'
match reqwest::get(&url).await {
    Ok(resp) => res.render(resp.text().await.unwrap_or_default()),
    Err(_) => res.status_code(StatusCode::INTERNAL_SERVER_ERROR),
}

}

The Secure Implementation

To kill SSRF, you must treat all outbound URL components as toxic. First, use the 'url' crate to parse the input; never use regex or manual string splitting. Second, implement a strict allowlist of domains. If your app requires dynamic URLs, you must resolve the hostname to an IP and verify it is not in a private range (e.g., 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16) or the loopback address. Finally, configure your HTTP client to disable redirects, as attackers often use 302 redirects to bypass initial domain checks.

SECURE CODE
use salvo::prelude::*;
use reqwest::Url;

#[handler] async fn secure_proxy(req: &mut Request, res: &mut Response) { let target = req.query::(“target”).unwrap_or_default(); let url = match Url::parse(&target) { Ok(u) => u, Err(_) => { res.status_code(StatusCode::BAD_REQUEST); return; } };

// 1. Enforce HTTPS only
if url.scheme() != "https" {
    res.status_code(StatusCode::FORBIDDEN);
    return;
}

// 2. Domain Allowlist validation
let allowed_domains = ["api.trusted-partner.com", "cdn.example.com"];
let host = url.host_str().unwrap_or("");
if !allowed_domains.contains(&host) {
    res.status_code(StatusCode::FORBIDDEN);
    res.render("Domain not authorized");
    return;
}

// 3. Hardened client: No redirects, short timeouts
let client = reqwest::Client::builder()
    .redirect(reqwest::redirect::Policy::none())
    .timeout(std::time::Duration::from_secs(5))
    .build().unwrap();

match client.get(url).send().await {
    Ok(resp) => res.render(resp.text().await.unwrap_or_default()),
    Err(_) => res.status_code(StatusCode::BAD_GATEWAY),
}

}

System Alert • ID: 1373
Target: Salvo API
Potential Vulnerability

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

74% of Salvo 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.