Webhooks explicados: cargas firmadas y estrategias de reintento
Por qué fallan los webhooks y cómo solucionarlo
Acabas de integrar el webhook de un proveedor de pagos para actualizar los estados de los pedidos en tu aplicación. Todo funciona en las pruebas, pero en producción algunos eventos nunca llegan o, peor aún, recibes duplicados. Sin las protecciones adecuadas, los webhooks pueden convertirse en una fuente de fallos silenciosos y agujeros de seguridad.
Los webhooks son callbacks HTTP desencadenados por eventos, pero a diferencia de las llamadas API típicas, se basan en push y son asíncronos. Esto introduce dos desafíos principales: verificar la autenticidad y garantizar la entrega. En esta guía, abordaremos ambos con cargas firmadas y reintentos inteligentes.
¿Qué son los webhooks?
Un webhook es un callback HTTP definido por el usuario. Cuando ocurre un evento en un sistema de origen (por ejemplo, un nuevo pago), envía una solicitud HTTP POST a una URL que proporciones, que contiene los datos del evento. Esto permite reacciones en tiempo real sin sondeo.
Los casos de uso comunes incluyen notificaciones de pago, disparadores de CI/CD e integraciones de chat. Pero como los webhooks atraviesan la internet pública, son vulnerables a la suplantación y a la pérdida.
Asegurar webhooks con cargas firmadas
¿Cómo sabes que un webhook proviene realmente de tu proveedor? Firmar la carga con un secreto compartido garantiza la integridad y la autenticidad.
Cómo funcionan las firmas HMAC
El proveedor calcula un HMAC (por ejemplo, SHA-256) del cuerpo de la solicitud usando una clave secreta y envía la firma en un encabezado (por ejemplo, X-Signature). Tu servidor recalcula el HMAC y lo compara usando una comparación de tiempo constante para prevenir ataques de temporización.
// 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');
return crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(digest));
}
Mejores prácticas:
- Usa un secreto fuerte y único por cada endpoint de webhook.
- Incluye una marca de tiempo en la carga firmada para prevenir ataques de repetición; rechaza solicitudes con más de unos minutos de antigüedad.
- Usa siempre HTTPS para proteger la carga en tránsito.
Implementar reintentos confiables
Incluso con firmas, los problemas de red o las caídas pueden hacer que falle la entrega de webhooks. Los proveedores suelen reintentar con retroceso exponencial, pero tú también debes manejar los reintentos de tu lado.
Idempotencia: la clave para reintentos seguros
Los reintentos pueden provocar un procesamiento duplicado. Usa claves de idempotencia (a menudo incluidas en la carga o como encabezado) para asegurar que cada evento se procese exactamente una vez. Almacena los IDs de eventos procesados e ignora los duplicados.
// Ejemplo: manejador de webhook idempotente
const processedEvents = new Set();
app.post('/webhook', (req, res) => {
const eventId = req.body.id;
if (processedEvents.has(eventId)) {
return res.status(200).send('Already processed');
}
// process event...
processedEvents.add(eventId);
res.status(200).send('OK');
});
Estrategia de reintento en el lado del receptor
Si tu procesamiento falla, responde con un estado 5xx para activar el reintento del proveedor. Para errores transitorios, también puedes implementar tu propia cola de reintentos con retroceso.
| Escenario | Respuesta | Acción |
|---|---|---|
| Éxito | 200 OK | Sin reintento |
| Error de validación | 400 Bad Request | Sin reintento (corregir carga) |
| Error del servidor | 500 Internal Server Error | El proveedor reintenta |
| Límite de tasa | 429 Too Many Requests | El proveedor reintenta tras un retraso |
Monitoreo y depuración de webhooks
Registra todas las solicitudes de webhook entrantes con encabezados y cargas (redacta datos sensibles). Usa una herramienta como el Formateador JSON para imprimir las cargas de forma legible y facilitar la inspección. Configura alertas para entregas fallidas y realiza un seguimiento de los recuentos de reintentos.
Preguntas frecuentes
¿Qué es una carga de webhook firmada?
Una carga firmada incluye una firma criptográfica (generalmente HMAC) que verifica que la solicitud provino de una fuente confiable y no fue alterada.
¿Cuántas veces debo reintentar un webhook fallido?
La mayoría de los proveedores reintentan de 3 a 5 veces con retroceso exponencial. Como receptor, responde con los códigos de estado adecuados y deja que el proveedor maneje los reintentos.
¿Puedo usar webhooks sin HTTPS?
No—HTTPS es esencial para cifrar la carga y prevenir ataques de intermediario. Nunca aceptes webhooks sobre HTTP simple.
¿Listo para inspeccionar y depurar cargas de webhooks? Prueba nuestro Formateador JSON gratuito para formatear y validar datos JSON al instante.