Webhooks explicados: cargas firmadas y reintentos

Backend2026-11-09TryQuickToolBox

Acabas de integrar una pasarela de pago o un servicio de CI/CD, y ahora necesitas recibir eventos en tiempo real. Los webhooks son la solución estándar, pero conllevan dos desafíos críticos: verificar que la solicitud proviene realmente del emisor y asegurar que los eventos no se pierdan si tu servidor no está disponible temporalmente. Este artículo explica cómo implementar cargas firmadas y mecanismos de reintento para construir un receptor de webhooks robusto.

¿Qué son los webhooks?

Un webhook es una devolución de llamada HTTP: en lugar de que tu aplicación consulte una API en busca de actualizaciones, el servicio envía una solicitud HTTP POST a una URL que proporcionas cada vez que ocurre un evento. Este modelo de inserción reduce la latencia y la carga del servidor. Sin embargo, como la solicitud se origina en un servicio externo, debes tratarla como una entrada no confiable.

¿Por qué firmar las cargas?

Sin verificación, cualquiera que descubra tu endpoint de webhook podría enviar eventos falsos, lo que potencialmente desencadenaría acciones no autorizadas. Firmar las cargas con un secreto compartido te permite confirmar la identidad del emisor y asegurar que la carga no ha sido manipulada. El método más común es HMAC (Hash-based Message Authentication Code) usando SHA-256.

Cómo funciona la firma HMAC

El emisor calcula un resumen HMAC del cuerpo crudo de la solicitud usando una clave secreta y lo incluye en una cabecera (por ejemplo, X-Signature). Tu servidor recalcula el HMAC con el mismo secreto y lo compara con la firma recibida. Si coinciden, la solicitud es auténtica.

// Ejemplo en Node.js: verificar la firma de un webhook
const crypto = require('crypto');

function verifySignature(payload, signature, secret) {
  const hmac = crypto.createHmac('sha256', secret);
  hmac.update(payload);
  const digest = hmac.digest('hex');
  // Usa timingSafeEqual para prevenir ataques de temporización
  return crypto.timingSafeEqual(
    Buffer.from(digest, 'hex'),
    Buffer.from(signature, 'hex')
  );
}

Importante: Siempre usa una función de comparación de tiempo constante (como timingSafeEqual) para evitar filtrar información a través de diferencias de temporización. Además, calcula el HMAC sobre el cuerpo crudo, no sobre el JSON parseado, porque la reserialización puede cambiar la secuencia de bytes.

Implementación de reintentos

Incluso con firmas, los problemas de red o los errores del servidor pueden hacer que falle la entrega del webhook. Un emisor confiable reintentará las entregas fallidas, típicamente con retroceso exponencial. Como receptor, debes diseñar tu endpoint para que sea idempotente y devolver códigos de estado apropiados.

Estrategia de reintentos desde la perspectiva del emisor

Diseño de un receptor idempotente

Debido a que los reintentos pueden causar entregas duplicadas, tu receptor debe manejar el mismo evento varias veces sin efectos adversos. Incluye un ID de evento único en la carga (o en la cabecera) y almacena los IDs procesados. Si un ID de evento ya ha sido procesado, devuelve un 200 OK inmediatamente sin reprocesar.

// Pseudocódigo para el manejo idempotente de webhooks
function handleWebhook(event) {
  if (processedEvents.has(event.id)) {
    return { status: 200, body: 'Already processed' };
  }
  // Procesar evento...
  processedEvents.add(event.id);
  return { status: 200, body: 'Success' };
}

Mejores prácticas para la seguridad y confiabilidad de webhooks

Comparación: Polling vs. Webhooks

AspectoPollingWebhooks
LatenciaDepende del intervaloCasi en tiempo real
Carga del servidorAlta (solicitudes frecuentes)Baja (solo en eventos)
ComplejidadSimple de implementarRequiere verificación de firma y manejo de reintentos
ConfiabilidadTú controlas los reintentosDepende de la política de reintentos del emisor

Preguntas frecuentes

¿Cómo verifico la firma de un webhook?

Calcula un HMAC del cuerpo crudo de la solicitud usando el secreto compartido, luego compáralo con la firma en la cabecera usando una función de comparación de tiempo constante.

¿Qué debería devolver mi endpoint de webhook?

Devuelve un código de estado 2xx (por ejemplo, 200 OK) tan pronto como hayas recibido el evento. Si devuelves 4xx o 5xx, el emisor podría reintentar o marcar la entrega como fallida.

¿Puedo usar webhooks sin firmas?

No se recomienda. Sin firmas, no puedes verificar al emisor, dejando tu endpoint vulnerable a solicitudes falsas. Siempre usa firmas en producción.

Al depurar cargas de webhooks, el formato JSON es esencial. Usa nuestro formateador JSON para imprimir y validar rápidamente los datos JSON entrantes.