Webhooks explicados: Payloads firmados y reintentos
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 riesgos: payloads no autenticados, eventos perdidos y entregas duplicadas. Este artículo explica cómo funcionan los webhooks, cómo firmar payloads para evitar manipulaciones y cómo implementar reintentos para una entrega confiable.
¿Qué son los webhooks?
Un webhook es una devolución de llamada HTTP definida por el usuario. En lugar de que tu aplicación consulte una API en busca de actualizaciones, el proveedor envía una solicitud HTTP POST a una URL que especifiques cada vez que ocurre un evento. Esto es más eficiente y permite reacciones casi en tiempo real.
Casos de uso comunes:
- Notificaciones de pago (p. ej., Stripe, PayPal)
- Estado de compilación de CI/CD (p. ej., GitHub, GitLab)
- Plataformas de mensajería (p. ej., Slack, Discord)
- Actualizaciones de CRM (p. ej., Salesforce)
¿Por qué firmar los payloads de los webhooks?
Los endpoints de webhooks son URLs de acceso público. Sin verificación, cualquiera puede enviar payloads falsos, lo que provoca corrupción de datos o brechas de seguridad. Firmar los payloads con un secreto compartido garantiza la autenticidad e integridad.
La mayoría de los proveedores utilizan HMAC (Hash-based Message Authentication Code) con SHA-256. El proveedor calcula una firma sobre el cuerpo sin procesar de la solicitud usando una clave secreta y la incluye en una cabecera (p. ej., X-Hub-Signature-256). Tu servidor recalcula la firma y la compara.
Cómo verificar una firma
Aquí tienes una guía paso a paso:
- Recupera el cuerpo sin procesar exactamente como se envió. No lo analices ni modifiques antes de la verificación.
- Extrae la firma de la cabecera.
- Calcula la firma esperada usando HMAC-SHA256 con tu secreto.
- Compara usando una función de tiempo constante para prevenir ataques de temporización.
Ejemplo en Node.js:
const crypto = require('crypto');
function verifySignature(payload, signature, secret) {
const hmac = crypto.createHmac('sha256', secret);
const digest = 'sha256=' + hmac.update(payload).digest('hex');
return crypto.timingSafeEqual(Buffer.from(digest), Buffer.from(signature));
}
En Python:
import hmac
import hashlib
def verify_signature(payload, signature, secret):
expected = hmac.new(secret.encode(), payload, hashlib.sha256).hexdigest()
return hmac.compare_digest(f'sha256={expected}', signature)
Utiliza siempre una comparación de tiempo constante para evitar filtrar información.
Implementar reintentos para la confiabilidad
Las redes fallan, los servidores se reinician y los despliegues ocurren. Un sistema de webhooks robusto debe reintentar las entregas fallidas. Los proveedores suelen reintentar con retroceso exponencial, pero también debes manejar los reintentos en tu lado receptor.
Estrategias de reintento
- Retroceso exponencial: Espera 1s, 2s, 4s, 8s, etc., entre reintentos.
- Intentos máximos: Limita los reintentos para evitar bucles infinitos (p. ej., 5 intentos).
- Cola de mensajes muertos: Tras los intentos máximos, almacena el evento para inspección manual.
- Idempotencia: Asegúrate de que procesar el mismo evento varias veces no cause efectos secundarios duplicados.
Claves de idempotencia
Muchos proveedores incluyen un ID de evento único (p. ej., X-Event-ID). Almacena los IDs procesados en una base de datos o caché para omitir duplicados. Por ejemplo, usa Redis con un TTL para rastrear los IDs vistos.
Comparación: Polling vs Webhooks
| Aspecto | Polling | Webhooks |
|---|---|---|
| Latencia | Depende del intervalo | Casi en tiempo real |
| Carga del servidor | Alta (solicitudes constantes) | Baja (solo en eventos) |
| Complejidad | Simple de implementar | Requiere endpoint, seguridad, reintentos |
| Confiabilidad | Pierde eventos entre sondeos | Puede perder si el endpoint está caído; los reintentos ayudan |
Mejores prácticas para consumidores de webhooks
- Responde rápido: Devuelve 2xx en pocos segundos; procesa de forma asíncrona.
- Valida las firmas: Verifica siempre antes de procesar.
- Registra todo: Guarda los payloads sin procesar y las cabeceras para depuración.
- Usa HTTPS: Nunca aceptes webhooks por HTTP simple.
- Monitorea los fallos: Configura alertas para fallos de reintento repetidos.
Preguntas frecuentes
¿Qué es una firma de webhook?
Una firma de webhook es un hash HMAC del payload, creado con un secreto compartido. Permite al receptor verificar que la solicitud provino del remitente esperado y no fue manipulada.
¿Cuántas veces debo reintentar un webhook fallido?
No hay un número universal, pero 3–5 reintentos con retroceso exponencial es lo común. Después de eso, registra el evento en una cola de mensajes muertos para revisión manual.
¿Puedo usar webhooks sin HTTPS?
Técnicamente sí, pero es altamente inseguro. Usa siempre HTTPS para cifrar los payloads y prevenir ataques de intermediario.
¿Listo para probar tus endpoints de webhooks? Usa nuestro JSON Formatter para inspeccionar y validar payloads rápidamente.