WooCommerce SHA256 webhook signature never validates

I get webhooks from a woocommerce site in a nodejs / express application. I am trying to verify the webhook signature to verify the authenticity, but the hash I compute never matches the signature that Woocommerce reports in the hook signature header.

Here is the code I use for authentication:

function verifySignature(signature, payload, key){ var computedSignature = crypto.createHmac("sha256", key).update(payload).digest('base64'); debug('computed signature: %s', computedSignature); return computedSignature === signature; } 

This function is called with the following parameters:

 var signature = req.headers['x-wc-webhook-signature']; verifySignature(signature, JSON.stringify(req.body), config.wooCommence.accounts.api[config.env].webhookSecret) 

The webhook header headers report the signature as BewIV/zZMbmuJkHaUwaQxjX8yR6jRktPZQN9j2+67Oo= . However, the result of the above operation is S34YqftH1R8F4uH4Ya2BSM1rn0H9NiqEA2Nr7W1CWZs=

I manually configured the secret in webhook, and, as you see in the code above, the same secret is also hardcoded in the express application. Therefore, either I take the wrong payload to calculate the signature, or there is something else suspicious that prevents me from checking this signature.

Would thank all the pointers that will help me solve this problem.

+4
source share
4 answers

An old question, but maybe it helps some poor soul. The signature must be verified against the body, not the JSON contained in it. i.e. raw body bytes.

pseudo:

  byte[] body = request.Body; string signature = request.Header["X-WC-Webhook-Signature"]; byte[] secretUtf8 = GetUtf8Bytes("yoursecrethere"); byte[] hash = HMAC_SHA256.ComputeHash(body, secretUtf8); string hashBase64 = ToBase64String(hash); bool isValid = hashBase64 == signature; 
+4
source

For people using node, this should do the trick.

 var processWebHookSignature = function (secret, body, signature) { signatureComputed = crypto.createHmac('SHA256', secret).update( new Buffer(JSON.stringify(body), 'utf8')).digest('base64'); return ( signatureComputed === signature ) ? true : false; } 
+4
source

I stumbled upon this while looking for a solution on having an Asp.NET app validation signature for a Woocommerce web hook. My answer is based on pseudo code provided by Johannes, which worked great. I applied the user controller attribute to intercept the request and verify 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 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<WooOrderModel>(jsonContent); //call a service to use the order data provided by WooCommerce _service.AddOrder(wooOrder); return "Success"; } } 

Note. Hashing code referenced this SO message .

+1
source

The hash must be calculated on a "raw body". When using the "express application" and using the original JSON bodyParser body, the "raw body" is lost, see How to access the unprocessed request body before bodyparser? , on the "raw body".

For instance:

 // 'misuse' verify option app.use(bodyParser.json({ verify: function(req,res,buf) { req.rawBody=buf; } })); var wcSignature = req.get('X-Wc-Webhook-Signature'); debug('wc signature: %s', wcSignature); var calculatedSignature = crypto.createHmac('SHA256', secret) .update(req.rawBody, 'utf8') .digest('base64'); debug('calculated signature: %s', calculatedSignature); 
0
source

All Articles