Fix SSRF (Server Side Request Forgery) in Poem
SSRF in the Poem framework typically manifests when a handler accepts a user-provided URL and fetches it using an HTTP client like `reqwest`. Without validation, an attacker can probe internal services, hit metadata endpoints (169.254.169.254), or bypass firewalls by forcing the server to act as a proxy.
The Vulnerable Pattern
use poem::{handler, web::Query, Route, listener::TcpListener, Server}; use serde::Deserialize;#[derive(Deserialize)] struct Params { url: String }
#[handler] async fn proxy(Query(res): Query
) -> String { // VULNERABLE: Direct use of user input in outbound request reqwest::get(&res.url).await.unwrap().text().await.unwrap() }
#[tokio::main] async fn main() { let app = Route::new().at(”/”, proxy); Server::new(TcpListener::bind(“127.0.0.1:3000”)).run(app).await.unwrap(); }
The Secure Implementation
The secure implementation mitigates SSRF by applying a multi-layered defense. First, it parses the input into a structured URL to prevent obfuscation attacks. Second, it enforces the HTTPS scheme to stop protocol smuggling (e.g., file:// or gopher://). Third, it implements a strict domain allowlist. For production-grade hardening, use a custom DNS resolver with the HTTP client to verify that the resolved IP address is not in a private or loopback range (e.g., 10.0.0.0/8, 127.0.0.1) before the request is dispatched, preventing DNS rebinding attacks.
use poem::{handler, web::Query, error::BadRequestError, Result}; use url::Url; use serde::Deserialize;#[derive(Deserialize)] struct Params { url: String }
fn validate_url(input: &str) -> Option
{ let parsed = Url::parse(input).ok()?; let host = parsed.host_str()?; // 1. Enforce HTTPS if parsed.scheme() != "https" { return None; } // 2. Strict Domain Allowlist let allowed_domains = ["api.trusted.com", "cdn.assets.io"]; if !allowed_domains.contains(&host) { return None; } Some(parsed)}
#[handler] async fn secure_proxy(Query(res): Query
) -> Result { let validated_url = validate_url(&res.url).ok_or(BadRequestError)?; let client = reqwest::Client::builder() .timeout(std::time::Duration::from_secs(5)) .build().map_err(|_| BadRequestError)?; let body = client.get(validated_url.as_str()) .send().await.map_err(|_| BadRequestError)? .text().await.map_err(|_| BadRequestError)?; Ok(body)
}
Your Poem API
might be exposed to SSRF (Server Side Request Forgery)
74% of Poem apps fail this check. Hackers use automated scanners to find this specific flaw. Check your codebase before they do.
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.