Fix Insecure Webhooks in Warp
Webhooks without cryptographic signature verification are essentially unauthenticated endpoints. If you're processing incoming POST requests from third-party services like Stripe or GitHub without verifying the 'X-Hub-Signature' (or equivalent), an attacker can spoof payloads to trigger unauthorized actions, such as bypassing payment checks or manipulating CI/CD pipelines. To secure Warp webhooks, you must validate the HMAC signature against the raw request body using a shared secret.
The Vulnerable Pattern
use warp::Filter;#[tokio::main] async fn main() { // VULNERABLE: No signature verification. // Anyone can POST any JSON to this endpoint. let webhook_route = warp::post() .and(warp::path(“webhook”)) .and(warp::body::json()) .map(|payload: serde_json::Value| { println!(“Processing untrusted payload: {:?}”, payload); warp::reply::with_status(“Received”, warp::http::StatusCode::OK) });
warp::serve(webhook_route).run(([127, 0, 0, 1], 3030)).await;
}
The Secure Implementation
To fix this, we implement three critical security controls: 1. Raw Body Access: We use `warp::body::bytes()` instead of `json()`. Verification must happen on the exact byte-for-byte payload received, as JSON re-serialization can change whitespace and break the hash. 2. HMAC-SHA256: We compute the hash of the body using the shared secret and compare it to the header provided by the provider. 3. Constant-Time Comparison: We use the `constant_time_eq` crate to compare the computed hash and the provided signature. This prevents timing attacks where an attacker could brute-force the signature byte-by-byte by measuring response times.
use warp::Filter; use hmac::{Hmac, Mac}; use sha2::Sha256; use constant_time_eq::constant_time_eq;type HmacSha256 = Hmac
; fn verify_signature(secret: &[u8], signature_header: &str, body: &[u8]) -> bool { let mut mac = HmacSha256::new_from_slice(secret).expect(“HMAC can take key of any size”); mac.update(body); let result = mac.finalize().into_bytes();
// Remove 'sha256=' prefix if present let clean_sig = signature_header.strip_prefix("sha256=").unwrap_or(signature_header); let decoded_sig = match hex::decode(clean_sig) { Ok(bytes) => bytes, Err(_) => return false, }; constant_time_eq(&result, &decoded_sig)}
#[tokio::main] async fn main() { let secret = b”your_webhook_secret_key”;
let secure_webhook = warp::post() .and(warp::path("webhook")) .and(warp::header::<String>("x-hub-signature-256")) .and(warp::body::bytes()) .map(move |sig: String, body: bytes::Bytes| { if verify_signature(secret, &sig, &body) { warp::reply::with_status("Authorized", warp::http::StatusCode::OK) } else { warp::reply::with_status("Forbidden", warp::http::StatusCode::FORBIDDEN) } }); warp::serve(secure_webhook).run(([127, 0, 0, 1], 3030)).await;
}
Your Warp API
might be exposed to Insecure Webhooks
74% of Warp 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.