How to fix Insecure Webhooks
in Poem
Executive Summary
Insecure webhooks in Poem are a critical vulnerability. Without cryptographic verification, an attacker can spoof events to trigger unauthorized actions, bypass business logic, or inject malicious data. Relying on IP whitelisting is a rookie mistake; you must implement HMAC signature verification to ensure the payload originated from a trusted source and wasn't tampered with in transit.
The Vulnerable Pattern
use poem::{post, handler, web::Json, Route, Server, endpoint::make_sync};#[handler] async fn insecure_webhook(payload: Json<serde_json::Value>) -> String { // VULNERABLE: The handler trusts any POST request to this endpoint. // An attacker can send a fake ‘order_paid’ event to unlock premium features. println!(“Processing event: {:?}”, payload); “Event Processed”.to_string() }
fn main() { let app = Route::new().at(“/webhook”, post(insecure_webhook)); }
The Secure Implementation
The secure implementation utilizes HMAC-SHA256 to validate the payload's integrity and authenticity. Key security controls implemented: 1. Raw Body Access: We extract the raw bytes from the request body before any JSON deserialization occurs, ensuring the signature is checked against the exact data sent. 2. Shared Secret: A high-entropy secret known only to the provider and the application is used. 3. Constant-Time Comparison: The 'verify_slice' method from the hmac crate is used to prevent timing attacks that could leak the signature. 4. Header Validation: The request is immediately rejected with a 401 Unauthorized if the signature header is missing or invalid.
use poem::{handler, post, web::Data, Request, Body, Route, http::StatusCode, Result}; use hmac::{Hmac, Mac}; use sha2::Sha256; use hex;type HmacSha256 = Hmac
; #[handler] async fn secure_webhook(req: &Request, mut body: Body) -> Result
{ let secret = std::env::var(“WEBHOOK_SECRET”).expect(“SECRET NOT SET”); // 1. Extract the signature header let signature_header = req.headers() .get("X-Hub-Signature-256") .and_then(|v| v.to_str().ok()) .ok_or(StatusCode::UNAUTHORIZED)?; // 2. Read the raw body bytes (crucial: do not parse JSON before verification) let body_bytes = body.into_bytes().await.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; // 3. Compute HMAC-SHA256 let mut mac = HmacSha256::new_from_slice(secret.as_bytes()) .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; mac.update(&body_bytes); // 4. Constant-time verification let expected_sig = hex::decode(signature_header.replace("sha256=", "")) .map_err(|_| StatusCode::BAD_REQUEST)?; mac.verify_slice(&expected_sig) .map_err(|_| StatusCode::UNAUTHORIZED)?; // Now it is safe to parse and process the payload Ok("Verified and Processed".to_string())
}
Your Poem API
might be exposed to Insecure Webhooks
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.