HIGH aspnetwebhook spoofing

Webhook Spoofing in Aspnet

How Webhook Spoofing Manifests in Aspnet

Webhook spoofing in Aspnet applications occurs when an attacker forges a webhook request to mimic a trusted third-party service (e.g., Stripe, GitHub, PayPal), tricking the application into processing malicious payloads as legitimate events. This commonly targets Aspnet Core Web API controllers that accept POST requests from external services without adequate request validation.

A typical vulnerable pattern involves a controller action that processes webhook payloads by only checking the Content-Type header or relying on a shared secret that is either hardcoded, leaked, or improperly verified. For example, an Aspnet Minimal API endpoint might look like this:

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.MapPost("/webhook/stripe", async (HttpRequest request) =>
{
    // Vulnerable: No signature verification
    using var reader = new StreamReader(request.Body);
    var json = await reader.ReadToEndAsync();
    var stripeEvent = JsonSerializer.Deserialize<StripeEvent>(json);

    if (stripeEvent?.Type == "payment_intent.succeeded")
    {
        // Process payment - attacker can spoof this event
        await ProcessPaymentAsync(stripeEvent.Data.Object);
    }
    return Results.Ok();
});

app.Run();

In this example, the endpoint accepts any POST to /webhook/stripe without verifying the Stripe-Signature header. An attacker can craft a fake payment_intent.succeeded event with arbitrary data (e.g., inflating the amount field) and send it directly to the endpoint, leading to unauthorized fund transfers or order fulfillment.

Similar risks exist in Aspnet MVC controllers using [HttpPost] attributes. Attackers exploit predictable webhook URLs (often logged or exposed via OpenAPI/Swagger) and lack of cryptographic validation to spoof events from services like GitHub (X-Hub-Signature-256), PayPal (transmission-sig), or custom services relying on shared secrets.

Aspnet-Specific Detection

Detecting webhook spoofing vulnerabilities in Aspnet applications requires validating that endpoints processing webhook requests implement proper request authentication mechanisms. middleBrick identifies these issues during its black-box scan by analyzing the unauthenticated attack surface for missing or weak validation of service-specific signatures, tokens, or headers.

During a scan, middleBrick sends a series of probes to webhook endpoints (discovered via path brute-forcing, OpenAPI spec analysis, or common patterns like /webhook/*, /api/*/webhook) and checks for:

  • Absence of signature verification (e.g., missing Stripe-Signature, X-Hub-Signature-256 headers in request)
  • Use of weak or static secrets (detected via timing analysis or response manipulation)
  • Acceptance of malformed or replayed payloads without nonce/timestamp validation
  • Overly permissive Content-Type checks that allow bypass (e.g., accepting text/plain when application/json is expected)

For example, if middleBrick scans an Aspnet Core application and finds an endpoint at https://api.example.com/webhook/paypal that returns 200 OK to a POST request with a fabricated transmission-sig header and a payload simulating a PAYMENT.SALE.COMPLETED event, it flags this as a potential webhook spoofing vulnerability under the "Input Validation" and "Authentication" security checks.

The scanner cross-references runtime behavior with any provided OpenAPI/Swagger spec. If the spec defines a webhook endpoint but lacks security requirements (e.g., no apiKey or http scheme specifying signature validation), middleBrick notes this as a documentation gap that may indicate missing protections.

Findings include severity ratings based on exploitability and impact (e.g., high if financial transactions can be spoofed), remediation guidance, and references to OWASP API Security Top 10 (API1:2023 Broken Object Level Authorization, API3:2023 Excessive Data Exposure, and API6:2023 Unrestricted Access to Sensitive Business Flows).

Aspnet-Specific Remediation

Fixing webhook spoofing in Aspnet applications involves implementing cryptographic request validation using the shared secret provided by the third-party service. Aspnet Core’s built-in features—such as middleware, filters, and cryptographic libraries—enable secure verification without external dependencies.

The recommended approach is to create a custom IAsyncActionFilter or middleware that validates the request signature before the controller action executes. Below is a secure implementation for Stripe webhooks in Aspnet Core:

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using Stripe;

public class ValidateStripeWebhookAttribute : TypeFilterAttribute
{
    public ValidateStripeWebhookAttribute() : base(typeof(ValidateStripeWebhookFilter))
    {
    }
}

public class ValidateStripeWebhookFilter : IAsyncActionFilter
{
    private readonly IConfiguration _config;
    public ValidateStripeWebhookFilter(IConfiguration config)
    {
        _config = config;
    }

    public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
    {
        var stripeSignature = context.HttpContext.Request.Headers["Stripe-Signature"];
        if (string.IsNullOrEmpty(stripeSignature))
        {
            context.Result = new UnauthorizedResult();
            return;
        }

        var webhookSecret = _config["Stripe:WebhookSecret"];
        if (string.IsNullOrEmpty(webhookSecret))
        {
            // Fail closed if secret not configured
            context.Result = new StatusCodeResult(500);
            return;
        }

        var request = context.HttpContext.Request;
        request.EnableBuffering();

        using var reader = new StreamReader(request.Body, leaveOpen: true);
        var json = await reader.ReadToEndAsync();
        request.Body.Position = 0; // Reset for downstream processing

        try
        {
            var stripeEvent = EventUtility.ConstructEvent(
                json,
                stripeSignature,
                webhookSecret,
                tolerance: 300 // 5-minute clock skew tolerance
            );

            // Attach validated event to HttpContext for controller use
            context.HttpContext.Items["StripeEvent"] = stripeEvent;
        }
        catch (StripeException)
        {
            context.Result = new BadRequestResult();
            return;
        }

        await next();
    }
}

Apply the filter to your webhook endpoint:

app.MapPost("/webhook/stripe", [ValidateStripeWebhook] async (HttpRequest request) =>
{
    var stripeEvent = request.HttpContext.Items["StripeEvent"] as StripeEvent;
    if (stripeEvent?.Type == "payment_intent.succeeded")
    {
        await ProcessPaymentAsync(stripeEvent.Data.Object);
    }
    return Results.Ok();
});

For GitHub webhooks, validate the X-Hub-Signature-256 header using HMAC-SHA256:

public class ValidateGitHubWebhookAttribute : TypeFilterAttribute
{
    public ValidateGitHubWebhookAttribute() : base(typeof(ValidateGitHubWebhookFilter))
    {
    }
}

public class ValidateGitHubWebhookFilter : IAsyncActionFilter
{
    private readonly IConfiguration _config;
    public ValidateGitHubWebhookFilter(IConfiguration config)
    {
        _config = config;
    }

    public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
    {
        var signature = context.HttpContext.Request.Headers["X-Hub-Signature-256"];
        if (string.IsNullOrEmpty(signature) || !signature.StartsWith("sha256="))
        {
            context.Result = new UnauthorizedResult();
            return;
        }

        var secret = _config["GitHub:WebhookSecret"];
        if (string.IsNullOrEmpty(secret))
        {
            context.Result = new StatusCodeResult(500);
            return;
        }

        var request = context.HttpContext.Request;
        request.EnableBuffering();

        using var reader = new StreamReader(request.Body, leaveOpen: true);
        var body = await reader.ReadToEndAsync();
        request.Body.Position = 0;

        using var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(secret));
        var hashBytes = hmac.ComputeHash(Encoding.UTF8.GetBytes(body));
        var hash = BitConverter.ToString(hashBytes).Replace("-", "").ToLowerInvariant();

        var expected = signature.Substring(7); // Remove "sha256=" prefix
        if (!CryptographicOperations.FixedTimeEquals(
                Encoding.ASCII.GetBytes(expected),
                Encoding.ASCII.GetBytes(hash)))
        {
            context.Result = new BadRequestResult();
            return;
        }

        await next();
    }
}

Key remediation principles:

  • Never rely on Content-Type or URL alone for trust.
  • Always verify cryptographic signatures using time-constant comparison to prevent timing attacks.
  • Enable request buffering (EnableBuffering()) only when necessary to avoid excessive memory use; reuse the stream after validation.
  • Store secrets in secure configuration (e.g., Azure Key Vault, user secrets, environment variables)—never in source code.
  • Implement replay attack protection by validating timestamps (included in Stripe’s tolerance parameter or GitHub’s X-GitHub-Delivery with cache).
  • Use Aspnet Core’s built-in CryptographicOperations.FixedTimeEquals for secure string comparison.

After applying these fixes, rescan with middleBrick to confirm the webhook endpoint now rejects spoofed requests with 401 Unauthorized or 400 Bad Request, indicating proper validation is in place.

Frequently Asked Questions

Does middleBrick test for webhook spoofing vulnerabilities in Aspnet applications?
Yes, middleBrick includes webhook spoofing detection as part of its Input Validation and Authentication security checks. During a scan, it probes unauthenticated webhook endpoints (e.g., those under /webhook/* or identified via OpenAPI) for missing or weak signature validation, replay attack susceptibility, and improper secret handling—providing actionable findings with severity ratings and remediation guidance specific to the detected issue.
Can I use middleBrick to verify that my Aspnet Core webhook endpoint properly validates Stripe signatures after implementing a fix?
Absolutely. After deploying your Aspnet Core application with signature validation middleware or filters, run a middleBrick scan against the webhook URL. The scanner will attempt to send forged webhook payloads (e.g., fake Stripe events with manipulated amounts) and verify whether the endpoint correctly rejects them due to missing or invalid Stripe-Signature header. A finding of "No webhook spoofing vulnerability detected" or a low risk score indicates effective protection.