I am receiving webhooks from a woocommerce site into a nodejs/express application. I am trying to verify the webhook\'s signature to prove authenticity, yet the hash I compu
I stumbled upon this while searching for a solution to have an Asp.NET application check signature of the Woocommerce web hook. My answer is based on the pseudo code Johannes provided which worked great. I implemented a custom controller attribute to intercept the request and check the signature before it hits the API controller method:
public class HmacSignatureFilter : ActionFilterAttribute
{
public override void OnActionExecuting(HttpActionContext actionContext)
{
var requestContent = actionContext.Request.Content;
var jsonContent = requestContent.ReadAsStringAsync().Result;
var byteContent = requestContent.ReadAsByteArrayAsync().Result;
//if the request contains this, it's the verification request from Woocommerce
//when the webhook is created so let it pass through so it can be verified
if (!jsonContent.Contains("webhook_id"))
{
var requestSignature = actionContext.Request.Headers;
var bodyHash = HashHMAC("test", byteContent); //this is the shared key between Woo and custom API. should be from config or database table.
var signature = actionContext.Request.Headers.GetValues("x-wc-webhook-signature");
if (bodyHash != signature.FirstOrDefault())
{
throw new HttpResponseException(HttpStatusCode.Forbidden);
}
}
base.OnActionExecuting(actionContext);
}
private static string HashHMAC(string key, byte[] message)
{
var keyBytes = Encoding.UTF8.GetBytes(key);
var hash = new HMACSHA256(keyBytes);
var computedHash = hash.ComputeHash(message);
return Convert.ToBase64String(computedHash);
}
}
Then to use the filter in your Api controller:
[RoutePrefix("api/woo")]
public class WooController : ApiController
{
public SomeService _service;
public WooController()
{
this._service = new SomeService();
}
// POST api/values
[Route("orderCreated")]
[HttpPost]
[HmacSignatureFilter]
public string Post()
{
var requestContent = Request.Content;
var jsonContent = requestContent.ReadAsStringAsync().Result;
//this is the test request from Woocommerce. Don't do anything but
//respond so it can verify the endpoint
if (jsonContent.Contains("webhook_id"))
{
return "Webhook Test Success";
}
var wooOrder = JsonConvert.DeserializeObject(jsonContent);
//call a service to use the order data provided by WooCommerce
_service.AddOrder(wooOrder);
return "Success";
}
}
Note: Hashing code was referenced from this SO post.