Ir al contenido

Verificación de la firma

Cada solicitud de webhook enviada por Sangho contiene una cabecera Sangho-Signature. Esta cabecera es una firma HMAC-SHA256 calculada con su webhook secret sobre el cuerpo sin procesar (raw) de la solicitud.

Por qué verificar la firma

  • Garantizar que la solicitud proviene realmente de Sangho.
  • Protegerse contra los ataques de repetición (replay attacks).
  • Validar la integridad del cuerpo de la solicitud.
  • Evitar el procesamiento de webhooks falsificados.

Formato de la cabecera

La cabecera Sangho-Signature contiene la firma HMAC-SHA256 del cuerpo en hexadecimal, con el prefijo sha256=. También se envían dos cabeceras complementarias:

CabeceraDescripción
Sangho-Signaturesha256=<hex> — firma HMAC del cuerpo sin procesar.
Sangho-Event-IDIdentificador único del evento: utilícelo para la deduplicación.
Sangho-TimestampMarca de tiempo Unix del envío: protege contra los replays.
Utilice siempre el cuerpo sin procesar

La firma se calcula sobre el cuerpo sin procesar (raw bytes) de la solicitud, antes de cualquier análisis de JSON. En Express.js, configure express.raw({ type: 'application/json' }): no utilice nunca express.json(), que transformaría el body antes de la verificación.

Buenas prácticas

  • Verificar la firma antes de procesar el evento.
  • Devolver HTTP 200 de inmediato y procesar en segundo plano.
  • Almacenar el Sangho-Event-ID para deduplicar las entregas múltiples.
  • Comparar las firmas con un método resistente a los timing attacks (timingSafeEqual, hmac.compare_digest, hash_equals).

Cabeceras de una solicitud de webhook

ResponseEjemplo de solicitud entrante
Cabeceras que Sangho envía a su endpoint con cada evento.
http
POST /webhook HTTP/1.1
Content-Type: application/json
Sangho-Signature: sha256=a2b3c4d5e6f7...
Sangho-Event-ID: evt_xxxxxxxxxxxx
Sangho-Timestamp: 1709294400

Verificación manual (openssl)

bash
echo -n "$PAYLOAD" | openssl dgst -sha256 -hmac "$WEBHOOK_SECRET" -hex
# Comparer avec la valeur dans Sangho-Signature (sans le préfixe sha256=)

Implementación por lenguaje

Cada ejemplo de al lado ilustra la verificación completa: lectura del cuerpo sin procesar, cálculo del HMAC-SHA256, comparación resistente a los timing attacks y, por último, análisis del JSON.

Para los SDK oficiales, utilice directamente el método auxiliar: sangho.webhooks.constructEvent(body, signature, secret) en javascript/JavaScript.

Responda rápido, procese en modo asíncrono

Su endpoint debe devolver un 200 en un máximo de 30 segundos. Encole la tarea en una cola asíncrona (Celery, Bull, Sidekiq…) y responda de inmediato. Pasado ese plazo, Sangho considera la entrega fallida y activa la política de reintentos.

Implementación por lenguaje

javascript
import express from 'express';
import crypto from 'crypto';


const app = express();


app.post(
  '/webhook',
  express.raw({ type: 'application/json' }),
  (req, res) => {
    const sig    = req.headers['sangho-signature'] as string;
    const secret = process.env.WEBHOOK_SECRET!;


    const expected = 'sha256=' + crypto
      .createHmac('sha256', secret)
      .update(req.body)
      .digest('hex');


    if (!crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected))) {
      return res.status(400).send('Signature invalide');
    }


    const event = JSON.parse(req.body.toString());
    // Traiter en arrière-plan, répondre immédiatement
    processEvent(event).catch(console.error);
    res.json({ received: true });
  },
);