Reputation: 1030
I am attempting to a validate the webhook transaction from WooCommerce on my Node.js website. However I cannot get the 2 strings to match.
I can see that the php signature is generated with the following code, and the source can be viewed here WooCommerce Source.
base64_encode( hash_hmac( $hash_algo, $payload, $this->get_secret(), true ) ));
I have noticed that if i turn off true
on the hash_hmac
, I can then get the 2 systems to create a match, however I would rather not edit the core of WooCommerce so I am wondering if there is something I am missing here?
For my Example I did edit the core and forced the payload to be the following, just so i could easily try and match the 2 systems
payload = '{"id":1,"etc":2,"host":"http:/\/localhost\/view-order\/8"}'
secret = 'welcome'
My code in Node.Js is the following.
var crypto = require('crypto');
hmac = crypto.createHmac('sha256', secret);
hmac.setEncoding('binary');
hmac.write(payload);
hmac.end();
hash = hmac.read();
result = base64.encode(hash);
console.log(result);
If I remove the url from the "host" JSON then it does work, is it something to do with the way it has been escaped? I think it may be an issue with the way PHP and node do the SHA256 hashing. I really can't workout exactly how to solve this.
Any help would be great,
Thanks
Upvotes: 2
Views: 2588
Reputation: 869
Extending @genau's answer:
You may ask, why is the issue happening!?
Almost all of us add the below code in our express app, which manipulates the actual req.body
, causing issues in authenticating hash signatures.
app.use(express.json());
How to handle this issue?
Add original req.body
to a new field (for eg: req.rawBody
), and use this field instead of req.body
while verifying the signature.
// This code will copy the req.body data to req.rawBody
app.use(
express.json({
verify: function (req, res, buf) {
req.rawBody = buf;
},
})
);
Later, you can verify the signature as shown below:
function requestHandler (req, res) {
const incomingSignature = req.headers["x-wc-webhook-signature"]; // modify this accordingly
const payload = req.rawBody; // Buffer, already, no need to stringify.
const calculatedSignature = crypto
.createHmac("sha256", "your-secret-key")
.update(payload)
.digest("base64");
const isValid = calculatedSignature === incomingSignature;
// ...
}
Thanks!
Upvotes: 0
Reputation: 194
I have run into a similar issue as you, using the code suggested here: SHA256 webhook signature from WooCommerce never verifies
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;
}
(Where body comes from req.body).
This only started working for me when I changed the way I obtain the raw body. I got it using the bodyParser middleware:
app.use(bodyParser.json({verify:function(req,res,buf){req.rawBody=buf}}))
(As explained in: https://github.com/expressjs/body-parser/issues/83#issuecomment-80784100)
So now instead of using
new Buffer(JSON.stringify(body), 'utf8')
I just use req.rawBody
I hope this solves your problems too.
Upvotes: 4