Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SHA256 webhook signature from WooCommerce never verifies

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 compute never corresponds to the signature that woocommerce reports in the hook's signature header.

Here is the code I am using to verify the authenticity:

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 being 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's signature headers reports the signature as BewIV/zZMbmuJkHaUwaQxjX8yR6jRktPZQN9j2+67Oo=. The result of the above operation, however, is S34YqftH1R8F4uH4Ya2BSM1rn0H9NiqEA2Nr7W1CWZs=

I have manually configured the secret on the webhook, and as you see in the code above, this same secret is also hardcoded in the express application. So either I am taking the wrong payload to compute the signature, or there is something else fishy that prevents me from verifying these signature.

Would appreciate any pointers to help me solve this issue.

like image 371
Luis Delgado Avatar asked Aug 20 '15 15:08

Luis Delgado


3 Answers

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;
}
like image 158
Julien Le Coupanec Avatar answered Nov 19 '22 13:11

Julien Le Coupanec


Since this is the top Google result for this question and there isn't a complete answer out there, here's a Python version using Flask that validates the WooCommerce webhook signature. It took a bit of trial and error, hope it helps someone out there:

import json
import base64
import hmac
import hashlib

from flask import Flask, request, Response

app = Flask(__name__)

# The WooCommerce webhook secret
WEBHOOK_SECRET = 'abc123456'

# Function that compares the computed signature to the one in the request
def verify_woocommerce_signature(body, signature, secret):
    digest = hmac.new(bytes(secret, 'utf-8'), body, hashlib.sha256).digest()
    encoded = base64.b64encode(digest).decode()

    return encoded == signature

# WooCommerce Order Creation Event
@app.route('/webhooks/woocommerce/order_created', methods=['POST'])
def webhooks_woocommerce_order_created():
    # Get raw request body
    body = request.get_data()
    
    # Get request signature
    signature = request.headers['X-WC-WEBHOOK-SIGNATURE']
    
    # Verify webhook signature and handle mismatch
    if verify_woocommerce_signature(body, signature, WEBHOOK_SECRET) is False:
        msg = {"success": False}
        return Response(json.dumps(msg), status=400, mimetype='application/json')

    # Signatures match, process the payload
like image 5
Brian Burton Avatar answered Nov 19 '22 13:11

Brian Burton


Old question but maybe it helps some poor soul out there. The signature needs to be checked against the body and not the JSON it contains. i.e. the raw bytes of the body.

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;
like image 4
Johannes Bildstein Avatar answered Nov 19 '22 12:11

Johannes Bildstein