GuardAPI Logo
GuardAPI
Automated Security Protocol

How to fix Insecure Webhooks
in ServiceStack

Executive Summary

Insecure webhooks are a goldmine for attackers. If your ServiceStack endpoint processes incoming webhooks without verifying the payload signature, you've effectively left a backdoor open. Attackers can forge events, bypass payment gateways, or trigger administrative actions by simply POSTing a crafted JSON body to your endpoint. To secure this, you must implement HMAC-based signature verification using a shared secret.

The Vulnerable Pattern

VULNERABLE CODE
[Route("/webhooks/process", "POST")]
public class ProcessWebhook : IReturnVoid
{
    public string EventType { get; set; }
    public Guid OrderId { get; set; }
}

public class WebhookService : Service { public void Post(ProcessWebhook request) { // VULNERABILITY: No source verification. // Anyone can send a POST request to this endpoint. if (request.EventType == “payment.succeeded”) { OrderManager.MarkAsPaid(request.OrderId); } } }

The Secure Implementation

The vulnerability lies in trusting the HTTP request source implicitly. The fix implements a 'Shared Secret' architecture. 1) Raw Body Access: We use Request.GetRawBodyAsync() because signature verification must be performed on the original payload, not a deserialized object which might have different formatting. 2) HMAC-SHA256: We compute a hash of the raw body using a secret key known only to the provider and our service. 3) Constant-Time Comparison: We use FixedTimeEquals to compare the computed signature against the header signature to mitigate side-channel timing attacks. If the signatures don't match, the request is discarded before any business logic executes.

SECURE CODE
[Route("/webhooks/process", "POST")]
public class ProcessWebhook : IReturnVoid { }

public class WebhookService : Service { private const string WebhookSecret = “whsec_f829…032”;

public async Task Post(ProcessWebhook request)
{
    // SECURE: Get raw body for cryptographic verification
    string json = await Request.GetRawBodyAsync();
    string headerSignature = Request.Headers["X-Webhook-Signature"];

    if (string.IsNullOrEmpty(headerSignature))
        throw HttpError.Unauthorized("Missing signature.");

    // Compute HMACSHA256 hash
    using (var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(WebhookSecret)))
    {
        var hash = hmac.ComputeHash(Encoding.UTF8.GetBytes(json));
        var computedSignature = BitConverter.ToString(hash).Replace("-", "").ToLower();

        // Constant-time comparison to prevent timing attacks
        if (!CryptographicOperations.FixedTimeEquals(
            Encoding.UTF8.GetBytes(computedSignature), 
            Encoding.UTF8.GetBytes(headerSignature)))
        {
            throw HttpError.Unauthorized("Invalid signature.");
        }
    }

    // Proceed only after verification
    var data = json.FromJson<ProcessWebhook>();
    if (data.EventType == "payment.succeeded")
    {
        OrderManager.MarkAsPaid(data.OrderId);
    }
}

}

System Alert • ID: 4981
Target: ServiceStack API
Potential Vulnerability

Your ServiceStack API might be exposed to Insecure Webhooks

74% of ServiceStack 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.