Verify webhooks in C#
⚠️
Preview — this feature is being built; the design below is the spec.
Check the syntic-signature header before trusting a webhook delivery. The header carries a timestamp t and one or more v1 signatures (several during secret rotation). The signature is the hex HMAC-SHA256 of t + "." + raw body, keyed with your endpoint’s signing secret. Reject the delivery if no v1 matches in constant time, or if t is more than 300 seconds away from your clock. Always hash the raw body bytes, before any JSON parsing.
using System.Security.Cryptography;
using System.Text;
public static class SynticWebhooks
{
public static bool Verify(string header, byte[] rawBody, string secret, DateTimeOffset now)
{
string? t = null;
var sigs = new List<string>();
foreach (var part in header.Split(','))
{
var kv = part.Trim().Split('=', 2);
if (kv.Length != 2) continue;
if (kv[0] == "t") t = kv[1];
else if (kv[0] == "v1") sigs.Add(kv[1]);
}
if (t is null || !long.TryParse(t, out var ts) || Math.Abs(now.ToUnixTimeSeconds() - ts) > 300) return false;
using var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(secret));
var signed = Encoding.UTF8.GetBytes(t + ".").Concat(rawBody).ToArray();
var expected = Encoding.ASCII.GetBytes(Convert.ToHexString(hmac.ComputeHash(signed)).ToLowerInvariant());
return sigs.Any(s => CryptographicOperations.FixedTimeEquals(Encoding.ASCII.GetBytes(s), expected));
}
}
// ASP.NET Core minimal API
app.MapPost("/syntic", async (HttpRequest req) =>
{
using var ms = new MemoryStream();
await req.Body.CopyToAsync(ms);
var ok = SynticWebhooks.Verify(req.Headers["syntic-signature"].ToString(), ms.ToArray(),
Environment.GetEnvironmentVariable("SYNTIC_WEBHOOK_SECRET")!, DateTimeOffset.UtcNow);
if (!ok) return Results.BadRequest();
Queue.Enqueue(ms.ToArray()); // process later
return Results.Ok();
});Convert.ToHexString returns upper case; lower-case it before comparing, since Syntic sends lower-case hex.
Test it
Send a test event to your endpoint, or recompute a signature by hand as shown on Webhooks.
Was this page helpful?