Webhook verification not working body_html has unicode characters

I have an Express.js Webhook consumer but it is failing to verify if the body_html of the product has unicode characters.

The below will verify:
“body_html”:“Intensiv, komplex, raffiniert und ausgewogen”

But this will fail to verify:
“body_html”:“\u003cp\u003e\u003cstrong\u003eIntensiv, komplex, raffiniert und ausgewogen \u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003e”

Below are the fundamental parts of the Webhook handler:

function validateShopifySignature() {
  return async (req, res, next) => {
    try {
      const rawBody = req.rawBody
      if (typeof rawBody == 'undefined') {
        throw new Error(
          'validateShopifySignature: req.rawBody is undefined. Please make sure the raw request body is available as req.rawBody.'
        )
      }

      const hmac = req.headers['x-shopify-hmac-sha256']
      const hash = crypto.createHmac('sha256', process.env.SHOPIFY_WEBHOOK_SIGNATURE).update(rawBody).digest('base64')
      const signatureOk = crypto.timingSafeEqual(Buffer.from(hash), Buffer.from(hmac))

      if (!signatureOk) {
        res.status(403).send('Unauthorized')

        return
      }

      next()
    } catch (err) {
      next(err)
    }
  }
}

app.use(
  express.json({
    limit: '50mb',
    verify: (req, res, buf) => {
      req.rawBody = buf
    },
  })
)

app.post('/webhook/product/update', validateShopifySignature(), (req, res, next) => {
  res.status(200).send('OK')
})

How can I get this to work even if body_html has unicode characters?

Hi @Jamal_Ali, this may depend on the middleware you are using. Here is a small sample express app which uses the express.raw middleware to parse incoming request bodies into buffers. I tested this with the unicode you provided and the HMAC validated as expected.

const express = require('express');
const crypto = require('crypto');
const app = express();

const secret = '***PUT IN YOUR SECRET KEY***';

app.use(express.raw({ type: '*/*' }));

app.post('*', (req, res) => {
  const shopifyHmac = req.headers['x-shopify-hmac-sha256'];
  const byteArray = req.body;
  const bodyString = byteArray.toString('utf8');

  const calculatedHmacDigest = crypto.createHmac('sha256', secret).update(byteArray).digest('base64');
  const hmacValid = crypto.timingSafeEqual(Buffer.from(calculatedHmacDigest), Buffer.from(shopifyHmac));

  console.log(`HMAC valid: ${hmacValid}, x-shopify-hmac-sha256: ${shopifyHmac}, calculated HMAC: ${calculatedHmacDigest}, body: ${bodyString}`);

  if (hmacValid) {
    res.send('HMAC validation successful.');
  } else {
    res.status(401).send('HMAC validation failed.');
  }
});

const PORT = 8080;
app.listen(PORT, () => {
  console.log(`Server is listening on port ${PORT}`);
});
1 Like