Ir al contenido

Verificar la firma de los webhooks

Ver como Markdown Lee esta página como texto plano Abrir en ChatGPT Haz preguntas sobre esta página Abrir en Claude Haz preguntas sobre esta página Abrir en Perplexity Haz preguntas sobre esta página

Todos los webhooks que te envía Verificamex van firmados con el esquema de Standard Webhooks. Al verificar la firma confirmas tres cosas antes de procesar el evento:

Origen

El webhook lo envió Verificamex: solo quien tiene tu clave puede generar una firma válida.

Integridad

El cuerpo no se modificó en el camino: cambiar un solo byte invalida la firma.

Vigencia

El envío es reciente: la hora del envío forma parte de lo firmado, así que puedes rechazar reenvíos viejos (replay).

Aplica a todos los webhooks: sesiones de verificación (V2, V3 y pasaporte), validación de INE asíncrona, constancia CURP, recibos CFE, Penny Test y procesos por lote.


HeaderEjemploDescripción
webhook-id9db97292-be25-4917-b046-ad3ed1bfd083_e2a1b4b396ef9927Identificador de la notificación. Es el mismo en todos los reintentos de esa notificación; úsalo para descartar duplicados.
webhook-timestamp1792085386Momento del envío en segundos Unix (UTC). Cambia en cada intento.
webhook-signaturev1,SEsPX3qWvq080fEnSEF4B1kyTTWalVxOFZSk/zhaXvk=Firma del envío con el prefijo de versión v1,. Durante un cambio de clave puede traer dos firmas separadas por un espacio.

  1. Inicia sesión en el panel de Verificamex Identidad.
  2. Entra a Configuración > Desarrollo > Firma de webhooks.
  3. Copia la Clave de firma con el botón Copiar. En pantalla se muestra parcialmente oculta (usa Mostrar para verla completa), pero el botón copia la clave completa. Tiene el formato whsec_ seguido de texto en Base64.
  4. Guárdala en tu servidor como un secreto, por ejemplo en la variable de entorno VERIFICAMEX_WEBHOOK_SECRET.

  1. Lee el cuerpo crudo de la petición, exactamente como llegó, antes de convertirlo a JSON.
  2. Revisa la hora: rechaza el envío si webhook-timestamp está a más de 5 minutos de tu hora actual.
  3. Arma el contenido firmado uniendo con puntos: {webhook-id}.{webhook-timestamp}.{cuerpo crudo}.
  4. Obtén la llave: quita el prefijo whsec_ a tu clave y decodifica el resto en Base64. La llave son esos bytes, no el texto de la clave.
  5. Calcula la firma: HMAC-SHA256 del contenido del paso 3 con la llave del paso 4, codificado en Base64.
  6. Compara: separa webhook-signature por espacios y quita el prefijo v1, a cada entrada. El webhook es válido si cualquiera coincide con tu firma. Usa una comparación de tiempo constante.
  7. Si ninguna coincide, responde 401 y no proceses el evento.

Opción 1: librería oficial de Standard Webhooks (recomendada)

Sección titulada «Opción 1: librería oficial de Standard Webhooks (recomendada)»

Las librerías del estándar hacen todos los pasos anteriores por ti, incluida la tolerancia de 5 minutos y la comparación con varias firmas. Están disponibles para varios lenguajes en standardwebhooks.com.

Ventana de terminal
npm install standardwebhooks
server.js
import express from 'express';
import { Webhook } from 'standardwebhooks';
const app = express();
const wh = new Webhook(process.env.VERIFICAMEX_WEBHOOK_SECRET);
// express.raw conserva el cuerpo crudo; no uses express.json() en esta ruta.
app.post('/webhooks/verificamex', express.raw({ type: 'application/json' }), (req, res) => {
let evento;
try {
evento = wh.verify(req.body.toString('utf8'), req.headers);
} catch (err) {
return res.status(401).send('Firma inválida');
}
// evento ya es el JSON del webhook, verificado.
res.sendStatus(200);
});

Si prefieres no agregar dependencias, estas funciones implementan los pasos de Cómo se verifica. Devuelven true solo si alguna firma coincide y el envío tiene menos de 5 minutos.

verificar-webhook.js
import crypto from 'node:crypto';
const TOLERANCIA_SEGUNDOS = 5 * 60;
export function verificarWebhook(rawBody, headers, secret) {
const id = headers['webhook-id'];
const timestamp = headers['webhook-timestamp'];
const firmas = headers['webhook-signature'];
if (!id || !timestamp || !firmas) return false;
const ahora = Math.floor(Date.now() / 1000);
if (Math.abs(ahora - Number(timestamp)) > TOLERANCIA_SEGUNDOS) return false;
const llave = Buffer.from(secret.replace(/^whsec_/, ''), 'base64');
const esperada = crypto
.createHmac('sha256', llave)
.update(`${id}.${timestamp}.`)
.update(rawBody)
.digest();
return firmas.split(' ').some((entrada) => {
const [version, valor] = entrada.split(',');
if (version !== 'v1' || !valor) return false;
const recibida = Buffer.from(valor, 'base64');
return recibida.length === esperada.length && crypto.timingSafeEqual(recibida, esperada);
});
}
server.js
app.post('/webhooks/verificamex', express.raw({ type: 'application/json' }), (req, res) => {
// req.body es un Buffer con el cuerpo crudo.
if (!verificarWebhook(req.body, req.headers, process.env.VERIFICAMEX_WEBHOOK_SECRET)) {
return res.status(401).send('Firma inválida');
}
const evento = JSON.parse(req.body.toString('utf8'));
res.sendStatus(200);
});

Si tu servidor no responde 200, Verificamex reintenta el envío. Un reintento trae el mismo webhook-id, con un webhook-timestamp y una firma nuevos. Guarda los webhook-id que ya procesaste: si te llega uno repetido, responde 200 y no vuelvas a ejecutar la acción.

Cada notificación nueva, por ejemplo cuando una sesión cambia de status, trae un webhook-id distinto.


Puedes generar una clave nueva en cualquier momento desde el panel, con el botón Regenerar clave en Configuración > Desarrollo > Firma de webhooks.

Revisa estos tres puntos. Son las causas de casi todos los problemas al cambiar la clave:

  • Tu verificación revisa todas las firmas del header. Durante el cambio, webhook-signature trae dos firmas y la primera es la de la clave nueva. Si tu código solo compara la primera, rechazará los webhooks en el momento en que regeneres la clave, aunque todavía tengas 24 horas para cambiarla. Las librerías de Standard Webhooks y los ejemplos de esta página ya revisan todas.
  • Puedes actualizar tu servidor en las próximas 24 horas. Pasado ese plazo, la clave actual deja de funcionar.
  • Sabes dónde está guardada la clave. Todos los servidores y ambientes que reciben webhooks de tu cuenta usan la misma clave; actualízalos todos.
  1. Presiona Regenerar clave. La ventana de confirmación te muestra la fecha y hora exactas en que dejará de funcionar tu clave actual. Marca la casilla de confirmación y presiona Regenerar clave.
  2. La clave nueva se activa de inmediato y aparece en pantalla. Desde ese momento, y durante 24 horas, cada webhook lleva dos firmas en webhook-signature: una con la clave nueva y otra con la anterior. El panel muestra hasta cuándo sigue funcionando la anterior.
  3. Copia la clave nueva y actualízala en tu servidor (por ejemplo en VERIFICAMEX_WEBHOOK_SECRET). Como tu verificación acepta el webhook si cualquiera de las firmas coincide, no pierdes envíos mientras haces el cambio.
  4. Pasadas las 24 horas, los webhooks se firman solo con la clave nueva.

Usa webhook.site como URL de webhook en una sesión de prueba para ver los headers webhook-id, webhook-timestamp y webhook-signature junto con el cuerpo exacto que envía Verificamex. Con esos valores y tu clave puedes probar tu función de verificación localmente.