Traducida del inglés. Si hay alguna diferencia, la página en inglés es la referencia. Leer en inglés

Verifica las firmas

Comprueba que un webhook viene realmente de Nembol, con verificadores listos para copiar en cuatro lenguajes.

Cada entrega lleva:

X-Nembol-Signature: t=1784900412,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd
  • t: timestamp Unix del intento de entrega.
  • v1: HMAC-SHA256, con el secreto de firma de tu endpoint como clave, sobre la cadena "<t>.<raw_body>". Durante el periodo de rotación del secreto puedes ver dos entradas v1=. Acepta la entrega si cualquiera coincide.

Pasos de verificación: (1) lee el cuerpo sin procesar de la petición, antes de cualquier parseo JSON o transformación de middleware; (2) calcula el HMAC esperado; (3) compara en tiempo constante; (4) rechaza si |now − t| supera los 5 minutos (protección contra replay).

import crypto from 'node:crypto';

// Express: app.post('/webhook', express.raw({ type: 'application/json' }), handler)
export function verifyNembolSignature(rawBody, signatureHeader, secret) {
  const parts = Object.fromEntries(
    signatureHeader.split(',').map((kv) => kv.split('=', 2)),
  );
  const t = Number(parts.t);
  if (!t || Math.abs(Date.now() / 1000 - t) > 300) return false;

  const expected = crypto
    .createHmac('sha256', secret)
    .update(`${t}.${rawBody}`)
    .digest('hex');

  return signatureHeader
    .split(',')
    .filter((kv) => kv.startsWith('v1='))
    .some((kv) => {
      const candidate = Buffer.from(kv.slice(3), 'hex');
      const exp = Buffer.from(expected, 'hex');
      return candidate.length === exp.length && crypto.timingSafeEqual(candidate, exp);
    });
}

El error clásico: verificar contra un cuerpo JSON re-serializado. El JSON que tu framework parsea y vuelve a convertir en texto no será idéntico byte a byte a lo que envió Nembol, y la firma no coincidirá. Conserva siempre los bytes originales.

Última actualización el 20 de octubre de 2026

¿Necesitas ayuda?

Escríbenos y te responde una persona, normalmente en un día laborable.