How to fix Shadow API Exposure
in Poem
Executive Summary
Shadow APIs represent the 'dark matter' of your attack surface—endpoints lurking in production that your security scanners and documentation don't see. In the Poem framework, this typically occurs when developers use catch-all wildcard routes for debugging or fail to synchronize their routing table with their OpenAPI specifications, leaving undocumented logic exposed to scanners like FFuf or Burp Suite.
The Vulnerable Pattern
use poem::{handler, route, Route, web::Path, get};#[handler] fn internal_debug(Path(cmd): Path
) -> String { format!(“Executing internal command: {}”, cmd) } #[handler] fn get_user() -> &‘static str { “User data” }
#[tokio::main] async fn main() { // VULNERABILITY: The ‘/debug’ wildcard is undocumented and exposed. // Attackers can fuzz this to find internal management functions. let app = Route::new() .at(“/api/user”, get(get_user)) .at(“/debug/*path”, get(internal_debug));
// Server::new(tcp_listener).run(app).await;
}
The Secure Implementation
To eliminate Shadow APIs in Poem, you must transition from manual `Route` definitions to `poem-openapi`. This enforces a schema-first approach where the code is the documentation. The fix involves: 1. Removing wildcard routes (`*path`) which act as catch-alls for undocumented logic. 2. Using `OpenApiService` to ensure every endpoint is explicitly declared in the OpenAPI spec. 3. Nesting sensitive routes under a specific prefix with mandatory authentication middleware, ensuring that even if an endpoint is 'hidden', it is not 'exposed' to unauthenticated actors.
use poem::{handler, route, Route, get, EndpointExt}; use poem_openapi::{OpenApi, OpenApiService};struct Api;
#[OpenApi] impl Api { /// Returns user data - Documented and tracked #[oai(path = “/user”, method = “get”)] async fn get_user(&self) -> &‘static str { “User data” } }
#[tokio::main] async fn main() { let api_service = OpenApiService::new(Api, “Secure API”, “1.0”) .server(“https://api.production.com/v1”);
let ui = api_service.swagger_ui(); // SECURE: Use poem-openapi to ensure the routing table matches the documentation. // Wildcards are removed. Internal routes are moved behind explicit middleware/auth. let app = Route::new() .nest("/v1", api_service) .nest("/docs", ui); // Server::new(tcp_listener).run(app).await;
}
Your Poem API
might be exposed to Shadow API Exposure
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.