GuardAPI Logo
GuardAPI
GuardAPI Logo GuardAPI

Fix Insecure Webhooks in Actix Web

Webhooks are often the weakest link in the chain. If you're blindly trusting POST bodies in Actix Web without cryptographic verification, you're inviting remote command execution or data corruption via spoofing. A hacker just needs your endpoint URL to push malicious state into your system. To secure this, you must implement HMAC-SHA256 signature verification using a shared secret.

The Vulnerable Pattern

use actix_web::{post, web, HttpResponse, Responder};
use serde::Deserialize;

#[derive(Deserialize)] struct WebhookPayload { event: String, user_id: u32 }

#[post(“/webhook”)] async fn insecure_webhook(payload: web::Json) -> impl Responder { // VULNERABILITY: No origin verification. Anyone can POST here. println!(“Processing event: {}”, payload.event); HttpResponse::Ok().body(“Processed”) }

The Secure Implementation

The secure implementation switches from web::Json to web::Bytes to capture the raw request body. This is critical because any re-serialization of a JSON object might change byte order or spacing, invalidating the HMAC signature. We use the 'hmac' and 'sha2' crates to compute a digest of the raw body using a pre-shared secret. The 'verify_slice' method performs a constant-time comparison, mitigating timing side-channel attacks that could allow an attacker to brute-force the signature byte-by-byte.

use actix_web::{post, web, HttpRequest, HttpResponse, Responder};
use hmac::{Hmac, Mac};
use sha2::Sha256;
use hex;

type HmacSha256 = Hmac;

#[post(“/webhook”)] async fn secure_webhook(req: HttpRequest, body: web::Bytes) -> impl Responder { let secret = “SUPER_SECRET_KEY”; let signature_hex = req.headers().get(“X-Hub-Signature-256”) .and_then(|h| h.to_str().ok()) .and_then(|s| s.strip_prefix(“sha256=”));

if signature_hex.is_none() { return HttpResponse::Unauthorized().finish(); }

let mut mac = HmacSha256::new_from_slice(secret.as_bytes()).expect("HMAC error");
mac.update(&body);

let sig_bytes = hex::decode(signature_hex.unwrap()).unwrap_or_default();

// Constant-time comparison to prevent timing attacks
if mac.verify_slice(&sig_bytes).is_ok() {
    let payload: serde_json::Value = serde_json::from_slice(&body).unwrap();
    return HttpResponse::Ok().body("Verified");
}

HttpResponse::Unauthorized().finish()

}

Protect your Actix Web API

Don't rely on manual checks. GuardAPI's Gemini 3 Pro engine detects Insecure Webhooks and logic flaws in seconds.

Run Automated Audit

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.