Webhooks explicados: cargas firmadas y reintentos

Backend2026-11-02TryQuickToolBox

Por qué los webhooks necesitan firmas y reintentos

Has creado un endpoint para recibir webhooks de un proveedor de pagos o una plataforma SaaS. Todo funciona en pruebas, pero en producción te enfrentas a dos problemas: ¿cómo sabes que la solicitud realmente proviene del proveedor? y ¿qué sucede cuando tu servidor está caído o lento? Sin cargas firmadas, cualquiera puede falsificar una solicitud. Sin reintentos, un solo fallo de red puede perder un evento para siempre.

Esta guía explica cómo implementar ambos correctamente. Cubriremos la firma HMAC, los pasos de verificación, las estrategias de reintentos y la idempotencia: los tres pilares del manejo confiable de webhooks.

Cómo funcionan las firmas de webhooks

Una firma de webhook es un hash criptográfico que demuestra que la carga fue creada por alguien que conoce un secreto compartido. La mayoría de los proveedores usan HMAC-SHA256. El emisor calcula la firma sobre el cuerpo crudo de la solicitud (a veces con una marca de tiempo) y la incluye en una cabecera. El receptor la recalcula y compara.

Este es un flujo típico:

  1. El proveedor y el consumidor comparten una clave secreta (configurada en el panel del proveedor).
  2. El proveedor crea la carga, calcula HMAC-SHA256(secret, payload) y la envía en una cabecera como X-Signature.
  3. El consumidor recibe la solicitud, lee el cuerpo crudo, recalcula el HMAC y compara usando una función de tiempo constante.
  4. Si coinciden, procesa el evento; de lo contrario, rechaza con 401.

Importante: Siempre usa el cuerpo crudo de la solicitud tal como se recibió, no una versión parseada y re-serializada. La serialización JSON puede cambiar los espacios en blanco y el orden de las claves, rompiendo la firma.

Añadir marcas de tiempo para prevenir ataques de repetición

Una carga firmada por sí sola puede ser reenviada por un atacante que capture una solicitud válida. Para mitigarlo, incluye una marca de tiempo en los datos firmados y rechaza solicitudes más antiguas que unos minutos. Por ejemplo:

signed_payload = timestamp + "." + raw_body
signature = HMAC-SHA256(secret, signed_payload)

El consumidor verifica que la marca de tiempo esté dentro de una ventana aceptable (por ejemplo, 5 minutos) y que la firma coincida.

Implementar la verificación de firmas

Veamos un ejemplo concreto en Python usando Flask. Verificaremos una firma enviada en la cabecera X-Signature.

import hmac
import hashlib
from flask import Flask, request, abort

app = Flask(__name__)
SECRET = b'your_shared_secret'

@app.route('/webhook', methods=['POST'])
def webhook():
    signature = request.headers.get('X-Signature')
    if not signature:
        abort(401)

    raw_body = request.get_data()
    expected = hmac.new(SECRET, raw_body, hashlib.sha256).hexdigest()

    if not hmac.compare_digest(expected, signature):
        abort(401)

    # Procesar el evento
    event = request.json
    # ... manejar evento ...
    return '', 200

Puntos clave:

Diseñar una estrategia de reintentos

Incluso con firmas, los webhooks pueden fallar: tu servidor podría estar caído, devolver un 500 o agotar el tiempo de espera. Los proveedores suelen reintentar con retroceso exponencial. Como consumidor, también debes manejar los reintentos con elegancia.

Esto es lo que debes considerar:

La mayoría de los proveedores reintentan ante respuestas no 2xx o tiempos de espera agotados, a menudo hasta por 24 horas con intervalos crecientes. Consulta la documentación de tu proveedor para más detalles.

Ejemplo: Manejador de eventos idempotente

Usa una tabla de base de datos para rastrear los IDs de eventos procesados. Aquí tienes un patrón simplificado:

def handle_event(event_id, payload):
    if db.exists('processed_events', event_id):
        return  # ya manejado
    # procesar carga
    db.insert('processed_events', event_id)
    # ... trabajo real ...

Para alto rendimiento, usa una restricción única en el ID del evento y captura los errores de clave duplicada.

Comparación: Webhooks firmados vs no firmados

AspectoCargas firmadasCargas no firmadas
SeguridadVerifica la autenticidad del emisorCualquiera puede falsificar solicitudes
Protección contra repeticiónPosible con marcas de tiempoNinguna
Esfuerzo de implementaciónModerado (HMAC + verificación de marca de tiempo)Bajo
Recomendado paraTodos los webhooks en producciónSolo pruebas internas

Errores comunes y mejores prácticas

Preguntas frecuentes

¿Qué es una carga de webhook firmada?

Una carga firmada incluye una firma criptográfica (generalmente HMAC) que permite al receptor verificar que la solicitud proviene de una fuente confiable y no fue manipulada.

¿Cómo manejo los reintentos de webhooks sin duplicar acciones?

Haz que tu manejador sea idempotente rastreando los IDs de eventos. Antes de procesar, verifica si el ID ya fue manejado; si es así, devuelve éxito sin repetir el trabajo.

¿Puedo usar webhooks sin firmas?

No se recomienda para producción. Sin firmas, no puedes distinguir solicitudes legítimas de falsificadas. Siempre verifica las firmas cuando estén disponibles.

Al depurar cargas de webhooks o analizar registros del servidor, el Formateador JSON puede ayudarte a inspeccionar y validar estructuras JSON rápidamente.