提问者:小点点

WooCommerce的SHA256 webhook签名从不验证


我正在从woocommerce网站接收webhooks,并将其转换为nodejs/express应用程序。我试图验证webhook的签名以证明其真实性,但我计算的哈希值从未与woocommerce报告的钩子签名头中的签名对应。

这是我用来验证真实性的代码:

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

正在使用以下参数调用此函数:

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

webhook的签名头将签名报告为beyov/zzmbmujkhauwaqxjx8yr6jrktpzqn9j267oo=。然而,上述操作的结果是S34YqftH1R8F4uH4Ya2BSM1rn0H9NiqEA2Nr7W1CWZs=

我在网络钩子上手动配置了秘密,正如您在上面的代码中看到的,相同的秘密也在Express应用程序中硬编码。因此,要么我使用错误的有效负载来计算签名,要么有其他可疑的东西阻止我验证这些签名。

如果有人能帮我解决这个问题,我将不胜感激。


共3个答案

匿名用户

对于使用node的人来说,这应该可以做到。

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;
}

匿名用户

这是个老问题,但也许它能帮助一些可怜的人。签名需要根据正文而不是它包含的JSON进行检查。i、 e.正文的原始字节。

伪:

        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;

匿名用户

我在寻找Asp解决方案时偶然发现了这一点。NET应用程序检查Woocommerce web挂钩的签名。我的答案基于Johannes提供的伪代码,该代码非常有效。我实现了一个自定义控制器属性来截获请求并在其到达API控制器方法之前检查签名:

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);
    }
}

然后要在Api控制器中使用过滤器:

[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";
    }

}

注意:此SO帖子引用了哈希代码。