¿Qué es la falsificación de webhooks (y por qué pasa)?

La falsificación de webhooks es cuando un atacante envía solicitudes falsas a tus endpoints “/webhooks/…” haciéndose pasar por el proveedor (pasarela de pago, Git, chat, etc.) para disparar acciones o robar datos.
 
Tu endpoint /webhooks/… es público. Cualquiera puede llamarlo. La única ‘identidad’ real es la firma y su frescura. Aquí te dejo cómo verificarla (con código), detectar replay y endurecer el endpoint sin afectar proveedores.
 

Solicita un pentest focalizado de webhooks:

pruebas técnicas + informe ejecutivo para gerencia.

El guion del atacante (qué hacen en la vida real).

  1. Descubre el endpoint: revisa documentación, repositorios y logs; enumera rutas con Burp/DirBuster.
  2. Clona el payload: copia un ejemplo público o intercepta uno real sin alterar orden ni codificación.
  3. Imita señales blandas: finge User-Agent, Content-Type e incluso IP “parecida” para saltar filtros débiles.
  4. Burla la verificación: aprovecha firma/timestamp mal validados o inexistentes.
    1. No se valida HMAC/clave pública contra el cuerpo crudo.
    2. Se aceptan algoritmos no permitidos o timestamps fuera de ventana.
    3. No hay antireplay: reenvía el mismo event-id/Idempotency-Key.
  5. Gatilla efectos: crea/activa usuarios, marca pagos como aprobados, dispara procesos o exfiltra datos.

Moraleja: ni HTTPS ni allowlists de IP prueban identidad. La diferencia entre proveedor real y atacante la marcan firma válida + frescura (timestamp) + antireplay, más validación de esquema y límites.

Agenda un “Webhook Health Check”:

validamos firmas, antireplay e idempotencia en 30 min.

Señales rojas (telemetría que debe encender alarmas)

  • IP fuera de rango del proveedor: solicitud desde redes no publicadas o ASN inesperado → bloquea y registra ASN/IP.
  • Timestamp fuera de ventana: fecha muy antigua o a futuro (±5 min) → responde 400 stale y registra desfase.
  • Firma inválida o ausente: cabeceras vacías, algoritmo no permitido, firma que no cuadra con el cuerpo crudo → 401 bad signature.
  • Replay detectado: Idempotency-Key o event-id repetido en corto tiempo → 409 replay y agrega a denylist temporal.
  • Burst anómalo: picos de tráfico hacia un endpoint específico o fuera del patrón habitual → 429 + backoff y alerta al equipo.
  • Esquema no coincide: tipo de evento declarado no corresponde al payload (campos faltantes/extras) → 422 unprocessable y traza el evento.
  • Mide siempre: firmas fallidas, replays, p95/p99 de latencia y tasa de 4xx/5xx por webhook.
  • Log mínimo por evento: event-id, digest, timestamp, resultado, IP/ASN, UA, latencia.

Cómo se defiende (lo que de verdad funciona)

A. Verificación criptográfica obligatoria

  • Firma requerida: HMAC (secreto compartido) o firma asimétrica (clave pública del proveedor). Sin firma = 401.
  • Qué verificar exactamente: cabecera de firma + timestamp + cuerpo crudo (sin re-serializar).
  • Algoritmos permitidos: HMAC-SHA256, Ed25519, RSA-SHA256 (bloquea SHA-1 y lo que no esté en allowlist).
  • Respuesta esperada:
    • Firma inválida/ausente → 401 Bad Signature
    • Algoritmo no permitido → 400 Unsupported Algorithm
  • Cabeceras típicas: Stripe-Signature, X-Hub-Signature-256, X-Signature, X-Timestamp.

 

Do: recalcular con el mismo byte stream recibido.
Don’t: firmar/verificar el JSON parseado.
 

B. Antireplay

  • Ventana de tiempo: tolerancia ±5 min entre now y el timestamp firmado.
  • Memoria de eventos: guarda event-id / Idempotency-Key (o hash del body+ts) 10–30 min.
  • One-and-done: un idempotency key/event-id se usa una vez; repetición = replay.
  • Respuesta esperada:
    • Timestamp fuera de ventana → 400 Stale
    • Evento repetido → 409 Replay Detected (añade a denylist temporal)

C. Hardening del endpoint

  • Solo POST con Content-Type esperado.
  • Ruta fija y secreta (no “/webhook” genérico).
  • Schema validation (JSON Schema/Protobuf).
  • Rate limit por proveedor; 429 + backoff.
  • mTLS opcional en B2B, si ambos lados lo soportan.
  • Rotación de secretos y claves públicas cacheadas con TTL.

D. Observabilidad

  • Log de (event-id, digest, ts, resultado, latencia, ip, UA).
  • Métricas: tasa de verificación fallida, replays, % latencia p95/p99, 5xx.

Agenda un “Webhook Health Check”:

validamos firmas, antireplay e idempotencia en 30 min.

Errores que veo a diario (y rompen integraciones)

  1. Confiar solo en IP allowlist (CDN/proxies cambian rangos).
  2. Leer el body parseado para firmar (pierdes bytes exactos).
  3. Aceptar cualquier algoritmo declarado por el cliente.
  4. No tener idempotencia: reintentos duplican efectos.
  5. “2xx siempre” para no romper al proveedor (esconde fallos reales).

Plantilla de runbook (incidente por falsificación)

  1. Bloquea endpoint (WAF / feature flag).
  2. Revoca secretos del webhook y rota claves.
  3. Habilita antireplay (cache de eventos) si no estaba activo.
  4. Reprocesa eventos legítimos desde la bitácora del proveedor.
  5. Post-mortem con métricas y pruebas automáticas.

Revisamos tus endpoints de webhook, configuramos firma + antireplay y te dejo una prueba automatizada lista.

Suscríbete a Nuestro Blog: Mantente actualizado con las últimas noticias y consejos en ciberseguridad. Suscríbete ahora.