WooCommerce Webhooks Auth (secreto y firma): cómo usar

4 minutos de lectura

avatar de usuario
Unicco

Estoy tratando de crear una integración entre API de webhook de WooCommerce y mi servidor Node.js. Sin embargo, realmente no puedo entender cómo se supone que debo usar el secreto para autenticar la solicitud.

secret: una clave secreta opcional que se utiliza para generar un HMAC-SHA256 hash del cuerpo de la solicitud para que el receptor pueda verificar la autenticidad del webhook.

X-WC-Webhook-Signature: un hash HMAC-SHA256 codificado en Base64 de la carga útil.

Back-end de WooCommerce:
(Hemmelighed = “Secreto”)
ingrese la descripción de la imagen aquí

Back-end de Nodejs:

var bodyParser = require('body-parser');
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));

router.post("https://stackoverflow.com/", function (req, res) {
    var secret="ciPV6gjCbu&efdgbhfgj&¤"#&¤GDA";
    var signature = req.header("x-wc-webhook-signature");
    var hash = CryptoJS.HmacSHA256(req.body, secret).toString(CryptoJS.enc.Base64);

    if(hash === signature){
        res.send('match');
    } else {
        res.send("no match");
    }

});

Fuente: https://github.com/woocommerce/woocommerce/pull/5941

Fuente API REST de WooCommerce

El hash y la firma no coinciden. ¿Qué está mal?

Actualizar:
console.log devuelve estos valores:

hash: pU9kXddJPY9MG9i2ZFLNTu3TXZA++85pnwfPqMr0dg0=

signature: PjKImjr9Hk9MmIdUMc+pEmCqBoRXA5f3Ac6tnji7exU=

hash (without .toString(CryptoJS.enc.Base64)): a54f645dd7493d8f4c1bd8b66452cd4eedd35d903efbce699f07cfa8caf4760d

  • ¿Estás tal vez usando bodyParser? req.body no es necesariamente una cadena.

    – dedo

    9 de enero de 2018 a las 16:09

  • @fingeron Sí, estoy usando bodyParser. He actualizado el codenippet. Probé var body = req.body.toString(‘utf8’); también, pero no ayuda.

    – Unicco

    9 de enero de 2018 a las 17:56


  • @Unicco ¿Agregarías console.log() tanto para el hash como para la firma? (hash sin toString() agregar con toString() )

    – gokcand

    9 de enero de 2018 a las 19:47

  • @gokcan y actualicé la pregunta.

    – Unicco

    09/01/2018 a las 20:39

  • @gokcand Perdón por la demora. Todavía no he tenido tiempo de comprobarlo. Implementé su solución ayer, pero recibí algunos errores. No tuve tiempo de arreglar esto. Examinaré más profundamente tu respuesta hoy. ¿Lo has probado tú mismo?

    – Unicco

    12 de enero de 2018 a las 11:01

avatar de usuario
gokcand

La firma debe verificarse con el cuerpo y no con el JSON que contiene. es decir los bytes sin procesar del req.body.

Modificar el bodyParser primero:

const rawBodySaver = (req, res, buf, encoding) => {
  if (buf && buf.length) {
    req.rawBody = buf.toString(encoding || 'utf8');
  }
};

app.use(bodyParser.json({ verify: rawBodySaver }));
app.use(bodyParser.urlencoded({ verify: rawBodySaver, extended: true }));
app.use(bodyParser.raw({ verify: rawBodySaver, type: '*/*' }));

y luego, usando cripto (se distribuye con un nodo que no necesita npm install cualquier cosa.)

import crypto from 'crypto'; //Let's try with built-in crypto lib instead of cryptoJS

router.post("https://stackoverflow.com/", function (req, res) {
  const secret="ciPV6gjCbu&efdgbhfgj&¤"#&¤GDA";
  const signature = req.header("X-WC-Webhook-Signature");

  const hash = crypto.createHmac('SHA256', secret).update(req.rawBody).digest('base64');

  if(hash === signature){
    res.send('match');
  } else {
    res.send("no match");
  }
});

  • Gracias. Necesitas cambiar req.header[“”] a req.header(“”). Lo siento, pero la firma y el hash no coinciden. Estas son las salidas después de implementar el búfer: hash: pU9kXddJPY9MG9i2ZFLNTu3TXZA++85pnwfPqMr0dg0= firma: 2Od/YW7laO4EtqdsO3CvOcXPTIeFCY5qVZdKdWSJcKU=

    – Unicco

    10 de enero de 2018 a las 8:40


  • Si elimino bodyparser, el req.body está vacío. Intenté cambiar la clave secreta privada a algo más simple, y esto tampoco ayuda. Creo que se requiere bodyparser para acceder al cuerpo; sin embargo, no estoy seguro de cómo puedo acceder a él de manera diferente.

    – Unicco

    10 de enero de 2018 a las 11:51


  • @Unicco Sí, es cierto que necesitamos bodyParser para acceder a req.body. He actualizado la respuesta, ¿puedes verificarla de nuevo? Especialmente, intente la segunda forma.

    – gokcand

    10 de enero de 2018 a las 13:10

  • Gracias. Probaré estos enfoques lo antes posible y te lo haré saber.

    – Unicco

    10 de enero de 2018 a las 14:49

Espero que a continuación le ahorre tiempo a alguien.

// 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!")
}

¿Ha sido útil esta solución?