GuardAPI Logo
GuardAPI

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;

}

System Alert • ID: 2121
Target: Warp API
Potential Vulnerability

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.

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.