How to fix Insecure Webhooks
in ASP.NET Core
Executive Summary
Webhooks are essentially unauthenticated entry points unless you enforce cryptographic integrity. Relying on obscurity or IP whitelisting is a failing strategy. In ASP.NET Core, an insecure webhook allows an attacker to forge events, escalate privileges, or poison the database by simply POSTing a JSON blob to your endpoint. To secure this, you must implement HMAC (Hash-based Message Authentication Code) verification to ensure the payload is authentic and untampered.
The Vulnerable Pattern
[HttpPost("api/webhooks/receive")] public async TaskInsecureWebhook() { using var reader = new StreamReader(Request.Body); var payload = await reader.ReadToEndAsync(); var data = JsonConvert.DeserializeObject (payload); // VULNERABILITY: No signature verification. // Anyone can POST any JSON to this endpoint. ProcessEvent(data); return Ok();
}
The Secure Implementation
The vulnerable code blindly trusts the HTTP request body, making it trivial to spoof. The secure implementation introduces three critical layers: 1. It retrieves a shared secret known only to the provider and the app. 2. It re-computes the HMAC SHA256 hash of the raw request body. 3. It uses CryptographicOperations.FixedTimeEquals for the comparison. This prevents side-channel timing attacks that could allow an attacker to brute-force the signature byte-by-byte. If the hashes don't match exactly, the request is discarded as malicious.
[HttpPost("api/webhooks/receive")] public async TaskSecureWebhook() { if (!Request.Headers.TryGetValue("X-Hub-Signature-256", out var signature)) return BadRequest("Missing signature"); Request.EnableBuffering(); using var reader = new StreamReader(Request.Body, leaveOpen: true); var payload = await reader.ReadToEndAsync(); Request.Body.Position = 0; var secret = Encoding.UTF8.GetBytes(_config["WebhookSecret"]); var payloadBytes = Encoding.UTF8.GetBytes(payload); using var hmac = new HMACSHA256(secret); var computedHash = hmac.ComputeHash(payloadBytes); var headerHash = Convert.FromHexString(signature.ToString().Replace("sha256=", "")); // Use FixedTimeEquals to prevent timing attacks if (!CryptographicOperations.FixedTimeEquals(computedHash, headerHash)) return Unauthorized("Invalid signature"); var data = JsonConvert.DeserializeObject<WebhookEvent>(payload); ProcessEvent(data); return Ok();
}
Your ASP.NET Core API
might be exposed to Insecure Webhooks
74% of ASP.NET Core 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.