来自WooCommerce的SHA256 webhook签名永远不会验证

时间:2015-08-20 15:47:18

标签: node.js hash woocommerce webhooks

我正在从一个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的签名标题将签名报告为BewIV/zZMbmuJkHaUwaQxjX8yR6jRktPZQN9j2+67Oo=。但是,上述操作的结果是S34YqftH1R8F4uH4Ya2BSM1rn0H9NiqEA2Nr7W1CWZs=

我已经在webhook上手动配置了秘密,正如您在上面的代码中所看到的,同样的秘密在Express应用程序中也是硬编码的。所以要么我采用错误的有效载荷来计算签名,要么还有其他可疑的东西阻止我验证这些签名。

非常感谢能帮我解决这个问题的任何指示。

7 个答案:

答案 0 :(得分:5)

对于使用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;
}

答案 1 :(得分:4)

老问题,但也许它可以帮助一些贫穷的灵魂。 需要针对正文检查签名,而不是它包含的JSON。即身体的原始字节。

伪:

        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;

答案 2 :(得分:2)

我在寻找具有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";
    }

}

注意:哈希代码是从this SO post引用的。

答案 3 :(得分:0)

哈希必须通过&#39; raw body计算。用于快递应用程序时。并使用JSON bodyParser中间件&#39; raw body&#39;迷路了,请How to access the raw body of the request before bodyparser?抓住原始身体&#39;。

例如:

// '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);

答案 4 :(得分:0)

希望能节省一些时间,以下对我有用。

// Make sure to add a WISTIA_SECRET_KEY in your Environment Variables
// See https://docs.pipedream.com/environment-variables/
const secret = process.env.SELF_AUTOMATE_KEY;
const signature = event.headers["x-wc-webhook-signature"];
const body = steps.trigger.raw_event["body_b64"];
const clean_Body = body.replace("body_b64: ", "");
//const body = steps.trigger.raw_event;
console.log(event.headers["x-wc-webhook-signature"]);

console.log("Print Body", clean_Body);

if (process.env.SELF_AUTOMATE_KEY === undefined) {
  $end("No WISTIA_SECRET_KEY environment variable defined. Exiting.")
}

if (!("x-wc-webhook-signature" in event.headers)) {
  $end("No x-wc-webhook-signature header present in the request. Exiting.")
}

// Once we've confirmed we have a signature, we want to 
// validate it by generating an HMAC SHA-256 hexdigest
const crypto = require('crypto');

const hash = crypto.createHmac('sha256',
  secret).update(JSON.stringify(clean_Body), 'base64').digest('base64');



console.log(hash);
// $end() ends the execution of a pipeline, presenting a nice message in the "Messages"
// column in the inspector above. See https://docs.pipedream.com/notebook/code/#end
if (hash !== signature) {
  $end("The correct secret key was not passed in the event. Exiting!")
}

答案 5 :(得分:0)

由于这是该问题的Google最高结果,而且还没有完整的答案,因此这是一个使用Flask的Python版本,可验证WooCommerce Webhook签名。经过一些反复试验,希望能对那里的人有所帮助:

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

答案 6 :(得分:0)

已在 TypeScript 中解决。我在 server.ts 中添加了这个:

this.app.use(bodyParser.json({
            verify: function(req, res, buf) {
                (req as any).rawBody = buf;
            }
        }));

之后:

 const computedSignature = crypto.createHmac("sha256", process.env.WOOCOMMERCE_SECRET).update((req as any).rawBody).digest("base64");