Como verificar que los webhooks son enviados por DEUNA

Cuando se integra con nuestra plataforma, es altamente recomendado verificar las firmas de los webhooks para asegurarse de que los eventos recibidos son enviados por nosotros y no por un tercero. Firmamos los eventos de webhook enviados a sus puntos finales incluyendo una firma en el encabezado llamado X-Deuna-Signature de cada evento. Esto le permite verificar la autenticidad de los eventos.

Importancia de la Verificación de Firmas de Webhook

Para proteger su servidor de eventos de webhook no autorizados, recomendamos el uso de firmas de Código de Autenticación de Mensajes Basado en Hash (HMAC usando el algoritmo SHA256) para nuestros webhooks que lo admiten. Para los webhooks que admiten habilitar firmas HMAC, cada evento incluirá una firma calculada utilizando una clave HMAC secreta y el payload del webhook. Al verificar esta firma, puede confirmar que:

  • El webhook fue enviado por nosotros.
  • El payload no fue modificado durante la transmisión.

Cómo Verificar la Firma del Webhook?

  1. Recuperar la Firma: Extraiga el encabezado X-Deuna-Signature de la solicitud entrante del webhook.
  2. Generar la Firma Esperada: Utilice la misma clave HMAC secreta (private API Key) y el payload del webhook para generar la firma esperada.
  3. Comparar Firmas: Compare la firma generada con la firma recibida. Si coinciden, el webhook es legítimo.

Advertencia: Para validar la firma, es necesario utilizar el raw body de la solicitud. Si estás usando un framework, asegúrate de que no manipule el raw body. Cualquier modificación en el raw body provocará que la validación falle.


Sugerencia: En el cuerpo de la notificación se encuentra el valor signed_at. Recomendamos verificar el tiempo transcurrido desde que se generó la firma hasta el momento actual, y según tus necesidades, realizar la validación correspondiente.

Código de Ejemplo para Verificar la Firma

Aquí hay ejemplos en varios lenguajes para ilustrar cómo verificar la firma HMAC SHA256:

Python

import hmac
import hashlib
import base64
import json
from django.http import HttpResponse


def validate_hmac_sha256_signature(secret, payload, signature):
    secret_bytes = bytes(secret, 'utf-8')
    payload_bytes = bytes(payload, 'utf-8')
    h = hmac.new(secret_bytes, payload_bytes, hashlib.sha256)
    expected_signature = base64.b64encode(h.digest()).decode('utf-8')
    return hmac.compare_digest(signature, expected_signature)

# Example
secret = "<your-private-api-key>"

# Using Django
@csrf_exempt
def my_webhook_view(request):
  payload = request.body
  sig_header = request.META['X-Deuna-Signature']
  
  if not validate_hmac_sha256_signature(secret, payload, sig_header):
    print("Invalid signature")
    return HttpResponse(status=403)

  return HttpResponse(status=201)

JS (Node.js)

const express = require('express');
const app = express();
const crypto = require('crypto');

const secret = "<your-private-api-key>"

function verifySignature(secret, signature, payload) {
  const hmac = crypto.createHmac('sha256', secret);
  const computedSignature = hmac.update(payload, 'utf8').digest('base64');
  return crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(computedSignature));
}

// Match the raw body to content type application/json
app.post('/webhook', express.raw({ type: 'application/json' }), (request, response) => {
  const sig = request.headers['x-deuna-signature'];
  const event = request.body;

  try {
    const verified = verifySignature(secret, sig, event);
    console.log('Verified:', verified);
  } catch (err) {
    console.error(err);
    response.status(400).send(`Webhook Error: ${err.message}`);
  }

  response.json({ received: true });
});

app.listen(4242, () => console.log('Running on port 4242'));
}