我正在从一个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应用程序中也是硬编码的。所以要么我采用错误的有效载荷来计算签名,要么还有其他可疑的东西阻止我验证这些签名。
非常感谢能帮我解决这个问题的任何指示。
答案 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");